From 120495773349fc4fb0bca48e4b03ea22fc5777c7 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Sun, 8 Nov 2026 17:50:31 +0100
Subject: [PATCH v1 1/2] Raise an error for invalid XML declarations in
 xmlconcat() and xmlroot()

Both functions strip the XML declaration of their input using
parse_xml_decl(), but ignored its result.  When the declaration could
not be parsed, the declaration length was left at the full length of
the value, so its content was silently dropped.  This also affected
xmlagg(), which uses xmlconcat().

Such values could be created by xmlroot() with an unusual VERSION, e.g.
'1.0"x'.  Raise the same error that xml_out() already reports as a
warning for them, instead of returning a wrong result.
---
 src/backend/utils/adt/xml.c       | 18 ++++++++++++++++--
 src/test/regress/expected/xml.out | 10 ++++++++++
 src/test/regress/sql/xml.sql      |  4 ++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 44a3a21d135..b6e7e367723 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -588,11 +588,18 @@ xmlconcat(List *args)
 		xmlChar    *version;
 		int			standalone;
 		char	   *str;
+		int			res_code;
 
 		len = VARSIZE(x) - VARHDRSZ;
 		str = text_to_cstring((text *) x);
 
-		parse_xml_decl((xmlChar *) str, &len, &version, NULL, &standalone);
+		res_code = parse_xml_decl((xmlChar *) str, &len, &version, NULL,
+								  &standalone);
+		if (res_code != 0)
+			ereport(ERROR,
+					errcode(ERRCODE_DATA_CORRUPTED),
+					errmsg_internal("could not parse XML declaration in stored value"),
+					errdetail_for_xml_code(res_code));
 
 		if (standalone == 0 && global_standalone == 1)
 			global_standalone = 0;
@@ -1091,11 +1098,18 @@ xmlroot(xmltype *data, text *version, int standalone)
 	xmlChar    *orig_version;
 	int			orig_standalone;
 	StringInfoData buf;
+	int			res_code;
 
 	len = VARSIZE(data) - VARHDRSZ;
 	str = text_to_cstring((text *) data);
 
-	parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone);
+	res_code = parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL,
+							  &orig_standalone);
+	if (res_code != 0)
+		ereport(ERROR,
+				errcode(ERRCODE_DATA_CORRUPTED),
+				errmsg_internal("could not parse XML declaration in stored value"),
+				errdetail_for_xml_code(res_code));
 
 	if (version)
 		orig_version = xml_text2xmlChar(version);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 6998745ee03..0a97c5cbe03 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -448,6 +448,16 @@ SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value)
  <?xml version="1.0" standalone="yes"?><foo/>
 (1 row)
 
+-- values with an XML declaration that cannot be parsed raise an error
+SELECT xmlconcat(xml '<foo/>', xmlroot(xml '<bar/>', version '1.0"x'));
+ERROR:  could not parse XML declaration in stored value
+DETAIL:  Parsing XML declaration: '?>' expected.
+SELECT xmlagg(x) FROM (VALUES (xml '<foo/>'), (xmlroot(xml '<bar/>', version '1.0"x'))) v(x);
+ERROR:  could not parse XML declaration in stored value
+DETAIL:  Parsing XML declaration: '?>' expected.
+SELECT xmlroot(xmlroot(xml '<bar/>', version '1.0"x'), version '1.0');
+ERROR:  could not parse XML declaration in stored value
+DETAIL:  Parsing XML declaration: '?>' expected.
 SELECT xmlroot (
   xmlelement (
     name gazonk,
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 0581d21b03f..66c2c93799e 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -116,6 +116,10 @@ SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone n
 SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no);
 SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no value);
 SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value);
+-- values with an XML declaration that cannot be parsed raise an error
+SELECT xmlconcat(xml '<foo/>', xmlroot(xml '<bar/>', version '1.0"x'));
+SELECT xmlagg(x) FROM (VALUES (xml '<foo/>'), (xmlroot(xml '<bar/>', version '1.0"x'))) v(x);
+SELECT xmlroot(xmlroot(xml '<bar/>', version '1.0"x'), version '1.0');
 
 
 SELECT xmlroot (
-- 
2.56.0

