xpath-parsing-error-fix.patch

text/x-patch

Filename: xpath-parsing-error-fix.patch
Type: text/x-patch
Part: 0
Message: Re: possible encoding issues with libxml2 functions

Patch

Format: unified
File+
src/backend/utils/adt/xml.c 8 1
src/test/regress/expected/xml_1.out 30 0
src/test/regress/expected/xml_2.out 26 0
src/test/regress/expected/xml.out 26 0
src/test/regress/sql/xml.sql 27 0
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index c47624eff6..5ceda8034d 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -3895,6 +3895,13 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 				 errmsg("empty XPath expression")));
 
 	string = pg_xmlCharStrndup(datastr, len);
+
+	/* It does nothing when string is in UTF8 already */
+	string = pg_do_encoding_conversion(string,
+									   len,
+									   GetDatabaseEncoding(),
+									   PG_UTF8);
+
 	xpath_expr = pg_xmlCharStrndup(VARDATA_ANY(xpath_expr_text), xpath_len);
 
 	xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
@@ -3911,7 +3918,7 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 		if (ctxt == NULL || xmlerrcxt->err_occurred)
 			xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
 						"could not allocate parser context");
-		doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0);
+		doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, "UTF-8", 0);
 		if (doc == NULL || xmlerrcxt->err_occurred)
 			xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
 						"could not parse XML document");
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index bcc585d427..0a5a80255f 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1452,3 +1452,29 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
  14
 (4 rows)
 
+-- XML is saved in database encoding with original encoding declaration.
+-- There can be incosistency based on wrong user input, different server/client
+-- encoding or reading XML with recv function. All XML functions should to
+-- work with this partially broken XML.
+DO $$
+DECLARE
+  str text;
+  result text;
+BEGIN
+  -- leave early without error, when we are not sure about result of conversion
+  IF current_setting('server_encoding') NOT IN ('UTF8', 'LATIN2') THEN return; END IF;
+
+  -- build valid UTF8 XML with broken encoding declaration
+  str = '<?xml version="1.0" encoding="windows-1250"?><enprimeur><vino><id>909</id><remark>'
+          || convert_from('\xf2', 'windows-1250')
+          || '</remark></vino></enprimeur>';
+
+  -- should not to raise XML parsing error
+  result := (xpath('/enprimeur/vino/id/text()', str::xml))[1];
+
+  -- should be quiet when result is expected
+  -- result be same for all encodings when all is ok
+  IF result <> '909' THEN
+    RAISE EXCEPTION 'unexpected result: %s', result;
+  END IF;
+END; $$;
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index d3bd8c91d7..9c20d384c9 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1302,3 +1302,33 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
 ---
 (0 rows)
 
+-- XML is saved in database encoding with original encoding declaration.
+-- There can be incosistency based on wrong user input, different server/client
+-- encoding or reading XML with recv function. All XML functions should to
+-- work with this partially broken XML.
+DO $$
+DECLARE
+  str text;
+  result text;
+BEGIN
+  -- leave early without error, when we are not sure about result of conversion
+  IF current_setting('server_encoding') NOT IN ('UTF8', 'LATIN2') THEN return; END IF;
+
+  -- build valid UTF8 XML with broken encoding declaration
+  str = '<?xml version="1.0" encoding="windows-1250"?><enprimeur><vino><id>909</id><remark>'
+          || convert_from('\xf2', 'windows-1250')
+          || '</remark></vino></enprimeur>';
+
+  -- should not to raise XML parsing error
+  result := (xpath('/enprimeur/vino/id/text()', str::xml))[1];
+
+  -- should be quiet when result is expected
+  -- result be same for all encodings when all is ok
+  IF result <> '909' THEN
+    RAISE EXCEPTION 'unexpected result: %s', result;
+  END IF;
+END; $$;
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+CONTEXT:  PL/pgSQL function inline_code_block line 15 at assignment
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index ff77132803..694163c7fb 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1432,3 +1432,29 @@ SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c
  14
 (4 rows)
 
+-- XML is saved in database encoding with original encoding declaration.
+-- There can be incosistency based on wrong user input, different server/client
+-- encoding or reading XML with recv function. All XML functions should to
+-- work with this partially broken XML.
+DO $$
+DECLARE
+  str text;
+  result text;
+BEGIN
+  -- leave early without error, when we are not sure about result of conversion
+  IF current_setting('server_encoding') NOT IN ('UTF8', 'LATIN2') THEN return; END IF;
+
+  -- build valid UTF8 XML with broken encoding declaration
+  str = '<?xml version="1.0" encoding="windows-1250"?><enprimeur><vino><id>909</id><remark>'
+          || convert_from('\xf2', 'windows-1250')
+          || '</remark></vino></enprimeur>';
+
+  -- should not to raise XML parsing error
+  result := (xpath('/enprimeur/vino/id/text()', str::xml))[1];
+
+  -- should be quiet when result is expected
+  -- result be same for all encodings when all is ok
+  IF result <> '909' THEN
+    RAISE EXCEPTION 'unexpected result: %s', result;
+  END IF;
+END; $$;
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index eb4687fb09..37b87ce5a2 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -558,3 +558,30 @@ INSERT INTO xmltest2 VALUES('<d><r><dc>2</dc></r></d>', 'D');
 SELECT xmltable.* FROM xmltest2, LATERAL xmltable('/d/r' PASSING x COLUMNS a int PATH '' || lower(_path) || 'c');
 SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH '.');
 SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH 'x' DEFAULT ascii(_path) - 54);
+
+-- XML is saved in database encoding with original encoding declaration.
+-- There can be incosistency based on wrong user input, different server/client
+-- encoding or reading XML with recv function. All XML functions should to
+-- work with this partially broken XML.
+DO $$
+DECLARE
+  str text;
+  result text;
+BEGIN
+  -- leave early without error, when we are not sure about result of conversion
+  IF current_setting('server_encoding') NOT IN ('UTF8', 'LATIN2') THEN return; END IF;
+
+  -- build valid UTF8 XML with broken encoding declaration
+  str = '<?xml version="1.0" encoding="windows-1250"?><enprimeur><vino><id>909</id><remark>'
+          || convert_from('\xf2', 'windows-1250')
+          || '</remark></vino></enprimeur>';
+
+  -- should not to raise XML parsing error
+  result := (xpath('/enprimeur/vino/id/text()', str::xml))[1];
+
+  -- should be quiet when result is expected
+  -- result be same for all encodings when all is ok
+  IF result <> '909' THEN
+    RAISE EXCEPTION 'unexpected result: %s', result;
+  END IF;
+END; $$;