Add XMLText function (SQL/XML X038)
authorDaniel Gustafsson <[email protected]>
Mon, 6 Nov 2023 08:38:29 +0000 (09:38 +0100)
committerDaniel Gustafsson <[email protected]>
Mon, 6 Nov 2023 08:38:29 +0000 (09:38 +0100)
This function implements the standard XMLTest function, which
converts text into xml text nodes. It uses the libxml2 function
xmlEncodeSpecialChars to escape predefined entities (&"<>), so
that those do not cause any conflict when concatenating the text
node output with existing xml documents.

This also adds a note in  features.sgml about not supporting
XML(SEQUENCE). The SQL specification defines a RETURNING clause
to a set of XML functions, where RETURNING CONTENT or RETURNING
SEQUENCE can be defined. Since PostgreSQL doesn't support
XML(SEQUENCE) all of these functions operate with an
implicit RETURNING CONTENT.

Author: Jim Jones <[email protected]>
Reviewed-by: Vik Fearing <[email protected]>
Discussion: https://postgr.es/m/86617a66-ec95-581f-8d54-08059cca8885@uni-muenster.de

doc/src/sgml/features.sgml
doc/src/sgml/func.sgml
src/backend/catalog/sql_features.txt
src/backend/utils/adt/xml.c
src/include/catalog/pg_proc.dat
src/test/regress/expected/xml.out
src/test/regress/expected/xml_1.out
src/test/regress/expected/xml_2.out
src/test/regress/sql/xml.sql

index 575afa34760c713081a9ea8768ef09a5864ce32e..966fd39882760786f1dc5f60df2de0d88663d854 100644 (file)
        standard.
       </para>
      </listitem>
+
+     <listitem>
+      <para>
+       <productname>PostgreSQL</productname> does not support the
+       <literal>RETURNING CONTENT</literal> or <literal>RETURNING SEQUENCE</literal>
+       clauses, functions which are defined to have these in the specification
+       are implicitly returning content.
+      </para>
+     </listitem>
     </itemizedlist>
    </para>
 
index a6fcac0824add8e13ab22c9dcabb15e2a1f26fac..d963f0a0a0000160e11ee75b22afd1affdd19046 100644 (file)
@@ -14180,6 +14180,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
+-------------------------
+ &lt; foo &amp; bar &gt;
+]]></screen>
+    </para>
+   </sect3>
+
    <sect3 id="functions-producing-xml-xmlcomment">
     <title><literal>xmlcomment</literal></title>
 
index b33065d7bf7c8034f3f3bb2b46c0ea8786392f0b..80c40eaf5780142bef83cc0f6bc03754cf5965c2 100644 (file)
@@ -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     supported except for RETURNING
 X040   Basic table mapping                     YES     
 X041   Basic table mapping: null absent                        YES     
 X042   Basic table mapping: null as nil                        YES     
index 2300c7ebf340c0034da17a26e1018404fabc3f49..c401e7b821986a9697efcda5be7f5ae8448a2a30 100644 (file)
@@ -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>
@@ -513,6 +514,27 @@ xmlcomment(PG_FUNCTION_ARGS)
 }
 
 
+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                                                 /* not USE_LIBXML */
+}
+
 
 /*
  * TODO: xmlconcat needs to merge the notations and unparsed entities
index 091f7e343c3da673036ea7fbd57304e1dadbfe39..f14aed422a765f3c3b6dff82f610c2964855630e 100644 (file)
 { 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',
index 398345ca67fe69939fec69b6ee6650fd708818a8..13e4296bf8ffbe1225824adcc06ac559b8443b2b 100644 (file)
@@ -1785,3 +1785,39 @@ SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH
  <foo/> | &lt;foo/&gt;
 (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 &amp; &lt;&quot;bar&quot;&gt;
+(1 row)
+
+SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+             xmltext             
+---------------------------------
+ x&lt;P&gt;73&lt;/P&gt;0.42truej
+(1 row)
+
index 63b779470ff6dcd09f4f4ac3b0b68da0ee622784..eb9c6f2ed4196ceea64f80e80b5853679e9245a2 100644 (file)
@@ -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.
index 43c2558352a3a21bb7c0a32a4508291507fbd3ca..c8ed8e0cfa60f525629c769c44d2c4e51c716097 100644 (file)
@@ -1765,3 +1765,39 @@ SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH
  <foo/> | &lt;foo/&gt;
 (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 &amp; &lt;&quot;bar&quot;&gt;
+(1 row)
+
+SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+             xmltext             
+---------------------------------
+ x&lt;P&gt;73&lt;/P&gt;0.42truej
+(1 row)
+
index a591eea2e5d14dec4acf4501d59ddfd8d6d70b25..bd4a4e7acdf8f4d5598a1525bb82d9e113ca01b0 100644 (file)
@@ -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