Skip to content

Commit 8c69ac3

Browse files
author
Commitfest Bot
committed
[CF 5718] v5 - contrib/xml2: xslt_process() should report XSLT-related error details and accept xmltype
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5718 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Robin Haberkorn
2 parents f8f4afe + a7b185f commit 8c69ac3

File tree

7 files changed

+247
-52
lines changed

7 files changed

+247
-52
lines changed

contrib/xml2/expected/xml2.out

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,33 @@ $$<xsl:stylesheet version="1.0"
261261
</xsl:template>
262262
</xsl:stylesheet>$$);
263263
ERROR: failed to apply stylesheet
264+
DETAIL: runtime error: file SQL line 7 element output
265+
File write for 0wn3d.txt refused
266+
runtime error: file SQL line 7 element output
267+
xsltDocumentElem: write rights for 0wn3d.txt denied
268+
-- detecting missing stylesheet parameter
269+
SELECT xslt_process('<xml/>',
270+
$$<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
271+
<template match="/">
272+
<value-of select="$n1"/>
273+
</template>
274+
</stylesheet>$$)::xml;
275+
ERROR: failed to apply stylesheet
276+
DETAIL: runtime error: file SQL line 3 element value-of
277+
Variable 'n1' has not been declared.
278+
Undefined variable
279+
runtime error: file SQL line 3 element value-of
280+
XPath evaluation returned no result.
281+
-- xmltype and Array-based signature
282+
SELECT xslt_process(xmlelement(name xml),
283+
$$<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
284+
<template match="/">
285+
<value-of select="$n1"/>
286+
</template>
287+
</stylesheet>$$::xml, ARRAY['n1','"foo"']);
288+
xslt_process
289+
--------------
290+
foo +
291+
292+
(1 row)
293+

contrib/xml2/sql/xml2.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,19 @@ $$<xsl:stylesheet version="1.0"
153153
</sax:output>
154154
</xsl:template>
155155
</xsl:stylesheet>$$);
156+
157+
-- detecting missing stylesheet parameter
158+
SELECT xslt_process('<xml/>',
159+
$$<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
160+
<template match="/">
161+
<value-of select="$n1"/>
162+
</template>
163+
</stylesheet>$$)::xml;
164+
165+
-- xmltype and Array-based signature
166+
SELECT xslt_process(xmlelement(name xml),
167+
$$<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
168+
<template match="/">
169+
<value-of select="$n1"/>
170+
</template>
171+
</stylesheet>$$::xml, ARRAY['n1','"foo"']);

contrib/xml2/xml2--1.1.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,14 @@ CREATE FUNCTION xslt_process(text,text)
7171
RETURNS text
7272
AS 'MODULE_PATHNAME'
7373
LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
74+
75+
CREATE FUNCTION xslt_process(xml,xml,text[])
76+
RETURNS xml
77+
AS 'MODULE_PATHNAME','xslt_process_xmltype'
78+
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
79+
80+
-- the function checks for the correct argument count
81+
CREATE FUNCTION xslt_process(xml,xml)
82+
RETURNS xml
83+
AS 'MODULE_PATHNAME','xslt_process_xmltype'
84+
LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;

contrib/xml2/xslt_proc.c

Lines changed: 134 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include "fmgr.h"
1111
#include "utils/builtins.h"
1212
#include "utils/xml.h"
13+
#include "utils/array.h"
14+
#include "utils/memutils.h"
15+
#include "mb/pg_wchar.h"
1316

1417
#ifdef USE_LIBXSLT
1518

@@ -35,9 +38,18 @@
3538
extern PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness);
3639

3740
/* local defs */
41+
static xmltype *xslt_process_internal(xmltype *doct, xmltype *ssheet, const char **params);
3842
static const char **parse_params(text *paramstr);
3943
#endif /* USE_LIBXSLT */
4044

45+
/*
46+
* FIXME: This cannot easily be exposed in xml.h.
47+
* Perhaps there should be an xml-internal.h?
48+
*/
49+
xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
50+
bool preserve_whitespace, int encoding,
51+
XmlOptionType *parsed_xmloptiontype, xmlNodePtr *parsed_nodes,
52+
Node *escontext);
4153

4254
PG_FUNCTION_INFO_V1(xslt_process);
4355

@@ -48,9 +60,103 @@ xslt_process(PG_FUNCTION_ARGS)
4860

4961
text *doct = PG_GETARG_TEXT_PP(0);
5062
text *ssheet = PG_GETARG_TEXT_PP(1);
51-
text *volatile result = NULL;
52-
text *paramstr;
53-
const char **params;
63+
const char **params = NULL;
64+
text *result;
65+
66+
if (fcinfo->nargs == 3)
67+
{
68+
text *paramstr = PG_GETARG_TEXT_PP(2);
69+
70+
params = parse_params(paramstr);
71+
}
72+
73+
result = xslt_process_internal(doct, ssheet, params);
74+
75+
PG_RETURN_TEXT_P(result);
76+
77+
#else /* !USE_LIBXSLT */
78+
79+
ereport(ERROR,
80+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
81+
errmsg("xslt_process() is not available without libxslt")));
82+
PG_RETURN_NULL();
83+
84+
#endif /* USE_LIBXSLT */
85+
}
86+
87+
PG_FUNCTION_INFO_V1(xslt_process_xmltype);
88+
89+
Datum
90+
xslt_process_xmltype(PG_FUNCTION_ARGS)
91+
{
92+
#ifdef USE_LIBXSLT
93+
94+
xmltype *doct = PG_GETARG_XML_P(0);
95+
xmltype *ssheet = PG_GETARG_XML_P(1);
96+
const char **params = NULL;
97+
xmltype *result;
98+
99+
/*
100+
* Parameters are key-value pairs. The values are XPath expressions, so
101+
* strings will have to be escaped with single or double quotes. Even
102+
* `xsltproc --stringparam` does nothing else than adding single or double
103+
* quotes and fails if the value contains both.
104+
*/
105+
if (fcinfo->nargs == 3)
106+
{
107+
ArrayType *paramarray = PG_GETARG_ARRAYTYPE_P(2);
108+
Datum *arr_datums;
109+
bool *arr_nulls;
110+
int arr_count;
111+
int i,
112+
j;
113+
114+
deconstruct_array_builtin(paramarray, TEXTOID, &arr_datums, &arr_nulls, &arr_count);
115+
116+
if ((arr_count % 2) != 0)
117+
ereport(ERROR,
118+
(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
119+
errmsg("number of stylesheet parameters (%d) must be a multiple of 2",
120+
arr_count)));
121+
122+
params = palloc_array(const char *, arr_count + 1);
123+
124+
for (i = 0, j = 0; i < arr_count; i++)
125+
{
126+
char *cstr;
127+
128+
if (arr_nulls[i])
129+
continue;
130+
131+
cstr = TextDatumGetCString(arr_datums[i]);
132+
params[j++] = (char *) pg_do_encoding_conversion((unsigned char *) cstr,
133+
strlen(cstr),
134+
GetDatabaseEncoding(),
135+
PG_UTF8);
136+
}
137+
params[j] = NULL;
138+
}
139+
140+
result = xslt_process_internal(doct, ssheet, params);
141+
142+
PG_RETURN_XML_P(result);
143+
144+
#else /* !USE_LIBXSLT */
145+
146+
ereport(ERROR,
147+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
148+
errmsg("xslt_process() is not available without libxslt")));
149+
PG_RETURN_NULL();
150+
151+
#endif /* USE_LIBXSLT */
152+
}
153+
154+
#ifdef USE_LIBXSLT
155+
156+
static xmltype *
157+
xslt_process_internal(xmltype *doct, xmltype *ssheet, const char **params)
158+
{
159+
text *volatile result;
54160
PgXmlErrorContext *xmlerrcxt;
55161
volatile xsltStylesheetPtr stylesheet = NULL;
56162
volatile xmlDocPtr doctree = NULL;
@@ -60,40 +166,39 @@ xslt_process(PG_FUNCTION_ARGS)
60166
volatile int resstat = -1;
61167
xmlChar *volatile resstr = NULL;
62168

63-
if (fcinfo->nargs == 3)
64-
{
65-
paramstr = PG_GETARG_TEXT_PP(2);
66-
params = parse_params(paramstr);
67-
}
68-
else
69-
{
70-
/* No parameters */
71-
params = (const char **) palloc(sizeof(char *));
72-
params[0] = NULL;
73-
}
169+
/* the previous libxslt error context */
170+
xmlGenericErrorFunc saved_errfunc;
171+
void *saved_errcxt;
74172

75173
/* Setup parser */
76-
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
174+
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_ALL);
175+
176+
/*
177+
* Save the previous libxslt error context.
178+
*/
179+
saved_errfunc = xsltGenericError;
180+
saved_errcxt = xsltGenericErrorContext;
181+
xsltSetGenericErrorFunc(xmlerrcxt, xml_generic_error_handler);
77182

78183
PG_TRY();
79184
{
80185
xmlDocPtr ssdoc;
81186
bool xslt_sec_prefs_error;
82187
int reslen = 0;
83188

84-
/* Parse document */
85-
doctree = xmlReadMemory((char *) VARDATA_ANY(doct),
86-
VARSIZE_ANY_EXHDR(doct), NULL, NULL,
87-
XML_PARSE_NOENT);
189+
/*
190+
* Parse document.
191+
*/
192+
doctree = xml_parse(doct, XMLOPTION_DOCUMENT, true,
193+
GetDatabaseEncoding(), NULL, NULL, NULL);
88194

89195
if (doctree == NULL || pg_xml_error_occurred(xmlerrcxt))
90196
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
91197
"error parsing XML document");
92198

93199
/* Same for stylesheet */
94-
ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet),
95-
VARSIZE_ANY_EXHDR(ssheet), NULL, NULL,
96-
XML_PARSE_NOENT);
200+
ssdoc = xml_parse(ssheet, XMLOPTION_DOCUMENT, true,
201+
GetDatabaseEncoding(), NULL, NULL, NULL);
97202

98203
if (ssdoc == NULL || pg_xml_error_occurred(xmlerrcxt))
99204
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
@@ -143,9 +248,10 @@ xslt_process(PG_FUNCTION_ARGS)
143248

144249
resstat = xsltSaveResultToString((xmlChar **) &resstr, &reslen,
145250
restree, stylesheet);
146-
147-
if (resstat >= 0)
148-
result = cstring_to_text_with_len((char *) resstr, reslen);
251+
if (resstat < 0 || pg_xml_error_occurred(xmlerrcxt))
252+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
253+
"failed to save result to string");
254+
result = cstring_to_text_with_len((char *) resstr, reslen);
149255
}
150256
PG_CATCH();
151257
{
@@ -163,6 +269,7 @@ xslt_process(PG_FUNCTION_ARGS)
163269
xmlFree(resstr);
164270
xsltCleanupGlobals();
165271

272+
xsltSetGenericErrorFunc(saved_errcxt, saved_errfunc);
166273
pg_xml_done(xmlerrcxt, true);
167274

168275
PG_RE_THROW();
@@ -179,24 +286,12 @@ xslt_process(PG_FUNCTION_ARGS)
179286
if (resstr)
180287
xmlFree(resstr);
181288

289+
xsltSetGenericErrorFunc(saved_errcxt, saved_errfunc);
182290
pg_xml_done(xmlerrcxt, false);
183291

184-
/* XXX this is pretty dubious, really ought to throw error instead */
185-
if (resstat < 0)
186-
PG_RETURN_NULL();
187-
188-
PG_RETURN_TEXT_P(result);
189-
#else /* !USE_LIBXSLT */
190-
191-
ereport(ERROR,
192-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
193-
errmsg("xslt_process() is not available without libxslt")));
194-
PG_RETURN_NULL();
195-
#endif /* USE_LIBXSLT */
292+
return result;
196293
}
197294

198-
#ifdef USE_LIBXSLT
199-
200295
static const char **
201296
parse_params(text *paramstr)
202297
{

doc/src/sgml/xml2.sgml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -408,22 +408,29 @@ ORDER BY doc_num, line_num;
408408
</indexterm>
409409

410410
<synopsis>
411-
xslt_process(text document, text stylesheet, text paramlist) returns text
411+
xslt_process(xml document, xml stylesheet, text[] paramlist) returns xml
412412
</synopsis>
413413

414414
<para>
415415
This function applies the XSL stylesheet to the document and returns
416-
the transformed result. The <literal>paramlist</literal> is a list of parameter
417-
assignments to be used in the transformation, specified in the form
418-
<literal>a=1,b=2</literal>. Note that the
419-
parameter parsing is very simple-minded: parameter values cannot
420-
contain commas!
416+
the transformed result. The <literal>paramlist</literal> is an array of parameter
417+
assignments to be used in the transformation, specified in pairs of
418+
key and value strings (e.g. <literal>ARRAY['a','1', 'b','2']</literal>).
419+
The length of the array must be even.
420+
Note that the values are still interpreted as XPath expressions, so string values need to
421+
be quoted in single or double quotes (e.g. <literal>ARRAY['a','"string"']</literal>).
421422
</para>
422423

423424
<para>
424425
There is also a two-parameter version of <function>xslt_process</function> which
425426
does not pass any parameters to the transformation.
426427
</para>
428+
429+
<para>
430+
<emphasis>Deprecated</emphasis> variants of <function>xslt_process</function> accepting
431+
text arguments and parameters encoded into single text strings
432+
(e.g. <literal>a=1,b=2</literal>) are also still available.
433+
</para>
427434
</sect3>
428435
</sect2>
429436

0 commit comments

Comments
 (0)