This adds the ability to pretty-print XML documents ... according to
libxml's somewhat idiosyncratic notions of what's pretty, anyway.
One notable divergence from a strict reading of the spec is that
libxml is willing to collapse empty nodes "<node></node>" to just
"<node/>", whereas SQL and the underlying XML spec say that this
option should only result in whitespace tweaks. Nonetheless,
it seems close enough to justify using the SQL-standard syntax.
Jim Jones, reviewed by Peter Smith and myself
Discussion: https://postgr.es/m/2f5df461-dad8-6d7d-4568-08e10608a69b@uni-muenster.de
@ -486,6 +486,192 @@ SELECT xmlserialize(content 'good' as char(10));
SELECT xmlserialize(document 'bad' as text);
ERROR: not an XML document
-- indent
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text INDENT);
xmlserialize
-------------------------
<foo> +
<bar> +
<val x="y">42</val>+
</bar> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text INDENT);
xmlserialize
-------------------------
<foo> +
<bar> +
<val x="y">42</val>+
</bar> +
</foo>
(1 row)
-- no indent
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
xmlserialize
-------------------------------------------
<foo><bar><val x="y">42</val></bar></foo>
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
xmlserialize
-------------------------------------------
<foo><bar><val x="y">42</val></bar></foo>
(1 row)
-- indent non singly-rooted xml
SELECT xmlserialize(DOCUMENT '<foo>73</foo><bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT '<foo>73</foo><bar><val x="y">42</val></bar>' AS text INDENT);
xmlserialize
-----------------------
<foo>73</foo> +
<bar> +
<val x="y">42</val>+
</bar>
(1 row)
-- indent non singly-rooted xml with mixed contents
SELECT xmlserialize(DOCUMENT 'text node<foo>73</foo>text node<bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT 'text node<foo>73</foo>text node<bar><val x="y">42</val></bar>' AS text INDENT);
xmlserialize
------------------------
text node +
<foo>73</foo>text node+
<bar> +
<val x="y">42</val> +
</bar>
(1 row)
-- indent singly-rooted xml with mixed contents
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><val x="y">text node<val>73</val></val></bar></foo>' AS text INDENT);
xmlserialize
---------------------------------------------
<foo> +
<bar> +
<val x="y">42</val> +
<val x="y">text node<val>73</val></val>+
</bar> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><val x="y">text node<val>73</val></val></bar></foo>' AS text INDENT);
xmlserialize
---------------------------------------------
<foo> +
<bar> +
<val x="y">42</val> +
<val x="y">text node<val>73</val></val>+
</bar> +
</foo>
(1 row)
-- indent empty string
SELECT xmlserialize(DOCUMENT '' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT '' AS text INDENT);
xmlserialize
--------------
(1 row)
-- whitespaces
SELECT xmlserialize(DOCUMENT ' ' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT ' ' AS text INDENT);
xmlserialize
--------------
(1 row)
-- indent null
SELECT xmlserialize(DOCUMENT NULL AS text INDENT);
xmlserialize
--------------
(1 row)
SELECT xmlserialize(CONTENT NULL AS text INDENT);
xmlserialize
--------------
(1 row)
-- indent with XML declaration
SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar><val>73</val></bar></foo>' AS text INDENT);
xmlserialize
----------------------------------------
<?xml version="1.0" encoding="UTF-8"?>+
<foo> +
<bar> +
<val>73</val> +
</bar> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar><val>73</val></bar></foo>' AS text INDENT);
xmlserialize
-------------------
<foo> +
<bar> +
<val>73</val>+
</bar> +
</foo>
(1 row)
-- indent containing DOCTYPE declaration
SELECT xmlserialize(DOCUMENT '<!DOCTYPE a><a/>' AS text INDENT);
xmlserialize
--------------
<!DOCTYPE a>+
<a/> +
(1 row)
SELECT xmlserialize(CONTENT '<!DOCTYPE a><a/>' AS text INDENT);
xmlserialize
--------------
<!DOCTYPE a>+
<a/> +
(1 row)
-- indent xml with empty element
SELECT xmlserialize(DOCUMENT '<foo><bar></bar></foo>' AS text INDENT);
xmlserialize
--------------
<foo> +
<bar/> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar></bar></foo>' AS text INDENT);
xmlserialize
--------------
<foo> +
<bar/> +
</foo>
(1 row)
-- 'no indent' = not using 'no indent'
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text) = xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
?column?
----------
t
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text) = xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
@ -309,6 +309,140 @@ ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(document 'bad' as text);
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- no indent
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent non singly-rooted xml
SELECT xmlserialize(DOCUMENT '<foo>73</foo><bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<foo>73</foo><bar><val x="y">4...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<foo>73</foo><bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<foo>73</foo><bar><val x="y">4...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent non singly-rooted xml with mixed contents
SELECT xmlserialize(DOCUMENT 'text node<foo>73</foo>text node<bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT 'text node<foo>73</foo>text nod...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT 'text node<foo>73</foo>text node<bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT 'text node<foo>73</foo>text nod...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent singly-rooted xml with mixed contents
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><val x="y">text node<val>73</val></val></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><val x="y">text node<val>73</val></val></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent empty string
SELECT xmlserialize(DOCUMENT '' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '' AS text INDENT);
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '' AS text INDENT);
^
DETAIL: This functionality requires the server to be built with libxml support.
-- whitespaces
SELECT xmlserialize(DOCUMENT ' ' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT ' ' AS text INDENT);
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT ' ' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT ' ' AS text INDENT);
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent null
SELECT xmlserialize(DOCUMENT NULL AS text INDENT);
xmlserialize
--------------
(1 row)
SELECT xmlserialize(CONTENT NULL AS text INDENT);
xmlserialize
--------------
(1 row)
-- indent with XML declaration
SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar><val>73</val></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar><val>73</val></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent containing DOCTYPE declaration
SELECT xmlserialize(DOCUMENT '<!DOCTYPE a><a/>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<!DOCTYPE a><a/>' AS text INDE...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<!DOCTYPE a><a/>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<!DOCTYPE a><a/>' AS text INDE...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- indent xml with empty element
SELECT xmlserialize(DOCUMENT '<foo><bar></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<foo><bar></bar></foo>' AS tex...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<foo><bar></bar></foo>' AS text INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<foo><bar></bar></foo>' AS tex...
^
DETAIL: This functionality requires the server to be built with libxml support.
-- 'no indent' = not using 'no indent'
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text) = xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text) = xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
ERROR: unsupported XML feature
LINE 1: SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><...
^
DETAIL: This functionality requires the server to be built with libxml support.
@ -466,6 +466,192 @@ SELECT xmlserialize(content 'good' as char(10));
SELECT xmlserialize(document 'bad' as text);
ERROR: not an XML document
-- indent
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text INDENT);
xmlserialize
-------------------------
<foo> +
<bar> +
<val x="y">42</val>+
</bar> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text INDENT);
xmlserialize
-------------------------
<foo> +
<bar> +
<val x="y">42</val>+
</bar> +
</foo>
(1 row)
-- no indent
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
xmlserialize
-------------------------------------------
<foo><bar><val x="y">42</val></bar></foo>
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
xmlserialize
-------------------------------------------
<foo><bar><val x="y">42</val></bar></foo>
(1 row)
-- indent non singly-rooted xml
SELECT xmlserialize(DOCUMENT '<foo>73</foo><bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT '<foo>73</foo><bar><val x="y">42</val></bar>' AS text INDENT);
xmlserialize
-----------------------
<foo>73</foo> +
<bar> +
<val x="y">42</val>+
</bar>
(1 row)
-- indent non singly-rooted xml with mixed contents
SELECT xmlserialize(DOCUMENT 'text node<foo>73</foo>text node<bar><val x="y">42</val></bar>' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT 'text node<foo>73</foo>text node<bar><val x="y">42</val></bar>' AS text INDENT);
xmlserialize
------------------------
text node +
<foo>73</foo>text node+
<bar> +
<val x="y">42</val> +
</bar>
(1 row)
-- indent singly-rooted xml with mixed contents
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val><val x="y">text node<val>73</val></val></bar></foo>' AS text INDENT);
xmlserialize
---------------------------------------------
<foo> +
<bar> +
<val x="y">42</val> +
<val x="y">text node<val>73</val></val>+
</bar> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val><val x="y">text node<val>73</val></val></bar></foo>' AS text INDENT);
xmlserialize
---------------------------------------------
<foo> +
<bar> +
<val x="y">42</val> +
<val x="y">text node<val>73</val></val>+
</bar> +
</foo>
(1 row)
-- indent empty string
SELECT xmlserialize(DOCUMENT '' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT '' AS text INDENT);
xmlserialize
--------------
(1 row)
-- whitespaces
SELECT xmlserialize(DOCUMENT ' ' AS text INDENT);
ERROR: not an XML document
SELECT xmlserialize(CONTENT ' ' AS text INDENT);
xmlserialize
--------------
(1 row)
-- indent null
SELECT xmlserialize(DOCUMENT NULL AS text INDENT);
xmlserialize
--------------
(1 row)
SELECT xmlserialize(CONTENT NULL AS text INDENT);
xmlserialize
--------------
(1 row)
-- indent with XML declaration
SELECT xmlserialize(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar><val>73</val></bar></foo>' AS text INDENT);
xmlserialize
----------------------------------------
<?xml version="1.0" encoding="UTF-8"?>+
<foo> +
<bar> +
<val>73</val> +
</bar> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<?xml version="1.0" encoding="UTF-8"?><foo><bar><val>73</val></bar></foo>' AS text INDENT);
xmlserialize
-------------------
<foo> +
<bar> +
<val>73</val>+
</bar> +
</foo>
(1 row)
-- indent containing DOCTYPE declaration
SELECT xmlserialize(DOCUMENT '<!DOCTYPE a><a/>' AS text INDENT);
xmlserialize
--------------
<!DOCTYPE a>+
<a/> +
(1 row)
SELECT xmlserialize(CONTENT '<!DOCTYPE a><a/>' AS text INDENT);
xmlserialize
--------------
<!DOCTYPE a>+
<a/> +
(1 row)
-- indent xml with empty element
SELECT xmlserialize(DOCUMENT '<foo><bar></bar></foo>' AS text INDENT);
xmlserialize
--------------
<foo> +
<bar/> +
</foo> +
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar></bar></foo>' AS text INDENT);
xmlserialize
--------------
<foo> +
<bar/> +
</foo>
(1 row)
-- 'no indent' = not using 'no indent'
SELECT xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text) = xmlserialize(DOCUMENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);
?column?
----------
t
(1 row)
SELECT xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text) = xmlserialize(CONTENT '<foo><bar><val x="y">42</val></bar></foo>' AS text NO INDENT);