XMLConcat() and XMLRoot() silently drop xml values with an invalid declaration

From: Jim Jones <jim(dot)jones(at)uni-muenster(dot)de>
To: PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: XMLConcat() and XMLRoot() silently drop xml values with an invalid declaration
Date: 2026-11-08 20:08:31
Message-ID: 757faa5a-dcfb-4878-8b94-aca25af17cc9@uni-muenster.de
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi

While investigating a libxml2-related crash I noticed that xmlroot()
does not validate VERSION,so it can store xml values whose declaration
xml_in() rejects, e.g. a version containing a quote or a non-ASCII
character. xmlconcat(),xmlagg() and xmlroot() then silently drop the
content of such values:

== master ==

postgres=# create table t (x xml);
CREATE TABLE
postgres=# insert into t values (xmlroot('<doc>data</doc>'::xml, version
'1.0"x'));
INSERT 0 1
postgres=# select xmlconcat('<a/>'::xml, x) from t;
xmlconcat
-----------
<a/>
(1 row)

Such values also cannot be restored from a dump, since xml_in() rejects
what xml_out() returns:

postgres=# select xml_in(xml_out(x)) from t;
WARNING: could not parse XML declaration in stored value
DETAIL: Parsing XML declaration: '?>' expected.
ERROR: invalid XML content: invalid XML declaration
DETAIL: Parsing XML declaration: '?>' expected.

v1 attached addresses these issues:

0001 makes xmlconcat() and xmlroot() raise an error for such values,
with the message xml_out() already warns with, instead of dropping their
content. Queries that currently return a wrong result for them will fail
instead, which IMHO is better. Affected rows can be found with ..

postgres=# select * from t where not xml_is_well_formed_content(x::text);
WARNING: could not parse XML declaration in stored value
DETAIL: Parsing XML declaration: '?>' expected.
x
----------------------------------------
<?xml version="1.0"x"?><doc>data</doc>
(1 row)

0002 makes xmlroot() reject a VERSION that cannot be read back unchanged
from the declaration it builds, so no new values like this can be
created. It does not otherwise check whether the version is valid. Since
0001's tests can then no longer create a bad value, 0002 replaces them.

With the patches applied, xmlroot() rejects such a version...

postgres=# select xmlroot('<doc>data</doc>'::xml, version '1.0"x');
ERROR: invalid XML version "1.0"x"

Thoughts?

Best, Jim

Attachment Content-Type Size
v1-0001-Raise-an-error-for-invalid-XML-declarations-in-xm.patch text/x-patch 4.3 KB
v1-0002-Reject-VERSION-values-in-xmlroot-that-break-the-X.patch text/x-patch 3.9 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Tomas Vondra 2026-11-08 21:07:19 reconsider_full_join_clause fails to consider if COALESCE() arguments are NULL-preserving
Previous Message Tomas Vondra 2026-11-08 19:47:03 Re: A gap in preventing bogus parameterizations in a798660ebe3