Skip to content

Commit

Permalink
Add XMLText function (SQL/XML X038)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
danielgustafsson committed Nov 6, 2023
1 parent 7b5275e commit 526fe0d
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 1 deletion.
9 changes: 9 additions & 0 deletions doc/src/sgml/features.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@
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>

Expand Down
30 changes: 30 additions & 0 deletions doc/src/sgml/func.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
2 changes: 1 addition & 1 deletion src/backend/catalog/sql_features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions src/backend/utils/adt/xml.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/include/catalog/pg_proc.dat
Original file line number Diff line number Diff line change
Expand Up @@ -8793,6 +8793,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',
Expand Down
36 changes: 36 additions & 0 deletions src/test/regress/expected/xml.out
Original file line number Diff line number Diff line change
Expand Up @@ -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)

23 changes: 23 additions & 0 deletions src/test/regress/expected/xml_1.out
Original file line number Diff line number Diff line change
Expand Up @@ -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.
36 changes: 36 additions & 0 deletions src/test/regress/expected/xml_2.out
Original file line number Diff line number Diff line change
Expand Up @@ -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)

7 changes: 7 additions & 0 deletions src/test/regress/sql/xml.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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);

0 comments on commit 526fe0d

Please sign in to comment.