Skip to content

Commit 526fe0d

Browse files
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 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 <jim.jones@uni-muenster.de> Reviewed-by: Vik Fearing <vik@postgresfriends.org> Discussion: https://postgr.es/m/86617a66-ec95-581f-8d54-08059cca8885@uni-muenster.de
1 parent 7b5275e commit 526fe0d

File tree

9 files changed

+167
-1
lines changed

9 files changed

+167
-1
lines changed

doc/src/sgml/features.sgml

+9
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@
199199
standard.
200200
</para>
201201
</listitem>
202+
203+
<listitem>
204+
<para>
205+
<productname>PostgreSQL</productname> does not support the
206+
<literal>RETURNING CONTENT</literal> or <literal>RETURNING SEQUENCE</literal>
207+
clauses, functions which are defined to have these in the specification
208+
are implicitly returning content.
209+
</para>
210+
</listitem>
202211
</itemizedlist>
203212
</para>
204213

doc/src/sgml/func.sgml

+30
Original file line numberDiff line numberDiff line change
@@ -14180,6 +14180,36 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
1418014180
documents for processing in client applications.
1418114181
</para>
1418214182

14183+
<sect3 id="functions-producing-xml-xmltext">
14184+
<title><literal>xmltext</literal></title>
14185+
14186+
<indexterm>
14187+
<primary>xmltext</primary>
14188+
</indexterm>
14189+
14190+
<synopsis>
14191+
<function>xmltext</function> ( <type>text</type> ) <returnvalue>xml</returnvalue>
14192+
</synopsis>
14193+
14194+
<para>
14195+
The function <function>xmltext</function> returns an XML value with a single
14196+
text node containing the input argument as its content. Predefined entities
14197+
like ampersand (<literal><![CDATA[&]]></literal>), left and right angle brackets
14198+
(<literal><![CDATA[< >]]></literal>), and quotation marks (<literal><![CDATA[""]]></literal>)
14199+
are escaped.
14200+
</para>
14201+
14202+
<para>
14203+
Example:
14204+
<screen><![CDATA[
14205+
SELECT xmltext('< foo & bar >');
14206+
xmltext
14207+
-------------------------
14208+
&lt; foo &amp; bar &gt;
14209+
]]></screen>
14210+
</para>
14211+
</sect3>
14212+
1418314213
<sect3 id="functions-producing-xml-xmlcomment">
1418414214
<title><literal>xmlcomment</literal></title>
1418514215

src/backend/catalog/sql_features.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ X034 XMLAgg YES
633633
X035 XMLAgg: ORDER BY option YES
634634
X036 XMLComment YES
635635
X037 XMLPI YES
636-
X038 XMLText NO
636+
X038 XMLText YES supported except for RETURNING
637637
X040 Basic table mapping YES
638638
X041 Basic table mapping: null absent YES
639639
X042 Basic table mapping: null as nil YES

src/backend/utils/adt/xml.c

+22
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
#ifdef USE_LIBXML
4949
#include <libxml/chvalid.h>
50+
#include <libxml/entities.h>
5051
#include <libxml/parser.h>
5152
#include <libxml/parserInternals.h>
5253
#include <libxml/tree.h>
@@ -513,6 +514,27 @@ xmlcomment(PG_FUNCTION_ARGS)
513514
}
514515

515516

517+
Datum
518+
xmltext(PG_FUNCTION_ARGS)
519+
{
520+
#ifdef USE_LIBXML
521+
text *arg = PG_GETARG_TEXT_PP(0);
522+
text *result;
523+
xmlChar *xmlbuf = NULL;
524+
525+
xmlbuf = xmlEncodeSpecialChars(NULL, xml_text2xmlChar(arg));
526+
527+
Assert(xmlbuf);
528+
529+
result = cstring_to_text_with_len((const char *) xmlbuf, xmlStrlen(xmlbuf));
530+
xmlFree(xmlbuf);
531+
PG_RETURN_XML_P(result);
532+
#else
533+
NO_XML_SUPPORT();
534+
return 0;
535+
#endif /* not USE_LIBXML */
536+
}
537+
516538

517539
/*
518540
* TODO: xmlconcat needs to merge the notations and unparsed entities

src/include/catalog/pg_proc.dat

+3
Original file line numberDiff line numberDiff line change
@@ -8793,6 +8793,9 @@
87938793
{ oid => '2922', descr => 'serialize an XML value to a character string',
87948794
proname => 'text', prorettype => 'text', proargtypes => 'xml',
87958795
prosrc => 'xmltotext' },
8796+
{ oid => '3813', descr => 'generate XML text node',
8797+
proname => 'xmltext', proisstrict => 't', prorettype => 'xml',
8798+
proargtypes => 'text', prosrc => 'xmltext' },
87968799

87978800
{ oid => '2923', descr => 'map table contents to XML',
87988801
proname => 'table_to_xml', procost => '100', provolatile => 's',

src/test/regress/expected/xml.out

+36
Original file line numberDiff line numberDiff line change
@@ -1785,3 +1785,39 @@ SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH
17851785
<foo/> | &lt;foo/&gt;
17861786
(1 row)
17871787

1788+
SELECT xmltext(NULL);
1789+
xmltext
1790+
---------
1791+
1792+
(1 row)
1793+
1794+
SELECT xmltext('');
1795+
xmltext
1796+
---------
1797+
1798+
(1 row)
1799+
1800+
SELECT xmltext(' ');
1801+
xmltext
1802+
---------
1803+
1804+
(1 row)
1805+
1806+
SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
1807+
xmltext
1808+
--------------------------
1809+
foo `$_-+?=*^%!|/\()[]{}
1810+
(1 row)
1811+
1812+
SELECT xmltext('foo & <"bar">');
1813+
xmltext
1814+
-----------------------------------
1815+
foo &amp; &lt;&quot;bar&quot;&gt;
1816+
(1 row)
1817+
1818+
SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
1819+
xmltext
1820+
---------------------------------
1821+
x&lt;P&gt;73&lt;/P&gt;0.42truej
1822+
(1 row)
1823+

src/test/regress/expected/xml_1.out

+23
Original file line numberDiff line numberDiff line change
@@ -1402,3 +1402,26 @@ DETAIL: This functionality requires the server to be built with libxml support.
14021402
SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
14031403
ERROR: unsupported XML feature
14041404
DETAIL: This functionality requires the server to be built with libxml support.
1405+
SELECT xmltext(NULL);
1406+
xmltext
1407+
---------
1408+
1409+
(1 row)
1410+
1411+
SELECT xmltext('');
1412+
ERROR: unsupported XML feature
1413+
DETAIL: This functionality requires the server to be built with libxml support.
1414+
SELECT xmltext(' ');
1415+
ERROR: unsupported XML feature
1416+
DETAIL: This functionality requires the server to be built with libxml support.
1417+
SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
1418+
ERROR: unsupported XML feature
1419+
DETAIL: This functionality requires the server to be built with libxml support.
1420+
SELECT xmltext('foo & <"bar">');
1421+
ERROR: unsupported XML feature
1422+
DETAIL: This functionality requires the server to be built with libxml support.
1423+
SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
1424+
ERROR: unsupported XML feature
1425+
LINE 1: SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j':...
1426+
^
1427+
DETAIL: This functionality requires the server to be built with libxml support.

src/test/regress/expected/xml_2.out

+36
Original file line numberDiff line numberDiff line change
@@ -1765,3 +1765,39 @@ SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH
17651765
<foo/> | &lt;foo/&gt;
17661766
(1 row)
17671767

1768+
SELECT xmltext(NULL);
1769+
xmltext
1770+
---------
1771+
1772+
(1 row)
1773+
1774+
SELECT xmltext('');
1775+
xmltext
1776+
---------
1777+
1778+
(1 row)
1779+
1780+
SELECT xmltext(' ');
1781+
xmltext
1782+
---------
1783+
1784+
(1 row)
1785+
1786+
SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
1787+
xmltext
1788+
--------------------------
1789+
foo `$_-+?=*^%!|/\()[]{}
1790+
(1 row)
1791+
1792+
SELECT xmltext('foo & <"bar">');
1793+
xmltext
1794+
-----------------------------------
1795+
foo &amp; &lt;&quot;bar&quot;&gt;
1796+
(1 row)
1797+
1798+
SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
1799+
xmltext
1800+
---------------------------------
1801+
x&lt;P&gt;73&lt;/P&gt;0.42truej
1802+
(1 row)
1803+

src/test/regress/sql/xml.sql

+7
Original file line numberDiff line numberDiff line change
@@ -660,3 +660,10 @@ SELECT * FROM XMLTABLE('*' PASSING '<e>pre<!--c1--><?pi arg?><![CDATA[&ent1]]><n
660660
\x
661661

662662
SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '"<foo/>"', b xml PATH '"<foo/>"');
663+
664+
SELECT xmltext(NULL);
665+
SELECT xmltext('');
666+
SELECT xmltext(' ');
667+
SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
668+
SELECT xmltext('foo & <"bar">');
669+
SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);

0 commit comments

Comments
 (0)