Hi Vik
Thanks for reviewing my patch!
On 25.08.23 12:05, Vik Fearing wrote:
I am replying to this email, but my comments are based on the v2 patch.
Thank you for working on this, and I think this is a valuable
addition. However, I have two issues with it.
1) There seems to be several spurious blank lines added that I do not
think are warranted.
I tried to copy the aesthetics of other functions, but it seems I failed
:) I removed a few blank lines. I hope it's fine now.
Is there any tool like pgindent to take care of it automatically?
2) This patch does nothing to address the <XML returning clause> so we
can't claim to implement X038 without a disclaimer. Upon further
review, the same is true of XMLCOMMENT() so maybe that is okay for
this patch, and a more comprehensive patch for our xml features is
necessary.
If we decide to not address this point here, I can take a look at it and
work in a separated patch.
v3 attached.
Thanks
Jim
From 5a5302c8c8a16bf7cf26ade70a40f9e016d23bbf Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jo...@uni-muenster.de>
Date: Fri, 25 Aug 2023 14:05:39 +0200
Subject: [PATCH v3] Add XMLText function (SQL/XML X038)
This function implements the standard XMLTest function, which
converts text into xml text nodes. It uses the libxml2 function
xmlEncodeSpecialChars to escape predifined entites (&"<>), so
that those do not cause any conflict when concatenating the text
node output with existing xml documents.
This patch includes also documentation and regression tests.
---
doc/src/sgml/func.sgml | 30 +++++++++++++++++++++++
src/backend/catalog/sql_features.txt | 2 +-
src/backend/utils/adt/xml.c | 26 ++++++++++++++++++++
src/include/catalog/pg_proc.dat | 3 +++
src/test/regress/expected/xml.out | 36 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 23 ++++++++++++++++++
src/test/regress/expected/xml_2.out | 36 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 7 ++++++
8 files changed, 162 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7a0d4b9134..2f01a2c25d 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14058,6 +14058,36 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
documents for processing in client applications.
</para>
+ <sect3 id="functions-producing-xml-xmltext">
+ <title><literal>xmltext</literal></title>
+
+ <indexterm>
+ <primary>xmltext</primary>
+ </indexterm>
+
+<synopsis>
+<function>xmltext</function> ( <type>text</type> ) <returnvalue>xml</returnvalue>
+</synopsis>
+
+ <para>
+ The function <function>xmltext</function> returns an XML value with a single
+ text node containing the input argument as its content. Predefined entities
+ like ampersand (<literal><![CDATA[&]]></literal>), left and right angle brackets
+ (<literal><![CDATA[< >]]></literal>), and quotation marks (<literal><![CDATA[""]]></literal>)
+ are escaped.
+ </para>
+
+ <para>
+ Example:
+<screen><![CDATA[
+SELECT xmltext('< foo & bar >');
+ xmltext
+-------------------------
+ < foo & bar >
+]]></screen>
+ </para>
+ </sect3>
+
<sect3 id="functions-producing-xml-xmlcomment">
<title><literal>xmlcomment</literal></title>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index b33065d7bf..680d541673 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -633,7 +633,7 @@ X034 XMLAgg YES
X035 XMLAgg: ORDER BY option YES
X036 XMLComment YES
X037 XMLPI YES
-X038 XMLText NO
+X038 XMLText YES
X040 Basic table mapping YES
X041 Basic table mapping: null absent YES
X042 Basic table mapping: null as nil YES
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 866d0d649a..dd8b453b71 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -47,6 +47,7 @@
#ifdef USE_LIBXML
#include <libxml/chvalid.h>
+#include <libxml/entities.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/tree.h>
@@ -505,6 +506,10 @@ xmlcomment(PG_FUNCTION_ARGS)
appendStringInfoText(&buf, arg);
appendStringInfoString(&buf, "-->");
+
+
+
+
PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
#else
NO_XML_SUPPORT();
@@ -5006,3 +5011,24 @@ XmlTableDestroyOpaque(TableFuncScanState *state)
NO_XML_SUPPORT();
#endif /* not USE_LIBXML */
}
+
+Datum
+xmltext(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *arg = PG_GETARG_TEXT_PP(0);
+ text *result;
+ xmlChar *xmlbuf = NULL;
+
+ xmlbuf = xmlEncodeSpecialChars(NULL,xml_text2xmlChar(arg));
+
+ Assert(xmlbuf);
+
+ result = cstring_to_text_with_len((const char *) xmlbuf, xmlStrlen(xmlbuf));
+ xmlFree(xmlbuf);
+ PG_RETURN_XML_P(result);
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif
+}
\ No newline at end of file
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9805bc6118..ff00c6365d 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8789,6 +8789,9 @@
{ oid => '2922', descr => 'serialize an XML value to a character string',
proname => 'text', prorettype => 'text', proargtypes => 'xml',
prosrc => 'xmltotext' },
+{ oid => '3813', descr => 'generate XML text node',
+ proname => 'xmltext', proisstrict => 't', prorettype => 'xml',
+ proargtypes => 'text', prosrc => 'xmltext' },
{ 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 398345ca67..13e4296bf8 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1785,3 +1785,39 @@ SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH
<foo/> | <foo/>
(1 row)
+SELECT xmltext(NULL);
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext('');
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext(' ');
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
+ xmltext
+--------------------------
+ foo `$_-+?=*^%!|/\()[]{}
+(1 row)
+
+SELECT xmltext('foo & <"bar">');
+ xmltext
+-----------------------------------
+ foo & <"bar">
+(1 row)
+
+SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+ xmltext
+---------------------------------
+ x<P>73</P>0.42truej
+(1 row)
+
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 63b779470f..eb9c6f2ed4 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1402,3 +1402,26 @@ DETAIL: This functionality requires the server to be built with libxml support.
SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmltext(NULL);
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext('');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmltext(' ');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmltext('foo & <"bar">');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+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.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 43c2558352..c8ed8e0cfa 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1765,3 +1765,39 @@ SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH
<foo/> | <foo/>
(1 row)
+SELECT xmltext(NULL);
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext('');
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext(' ');
+ xmltext
+---------
+
+(1 row)
+
+SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
+ xmltext
+--------------------------
+ foo `$_-+?=*^%!|/\()[]{}
+(1 row)
+
+SELECT xmltext('foo & <"bar">');
+ xmltext
+-----------------------------------
+ foo & <"bar">
+(1 row)
+
+SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+ xmltext
+---------------------------------
+ x<P>73</P>0.42truej
+(1 row)
+
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index a591eea2e5..bd4a4e7acd 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -660,3 +660,10 @@ SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n
\x
SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
+
+SELECT xmltext(NULL);
+SELECT xmltext('');
+SELECT xmltext(' ');
+SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
+SELECT xmltext('foo & <"bar">');
+SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
\ No newline at end of file
--
2.34.1