Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5400f39

Browse files
author
Commitfest Bot
committed
[CF 5718] v3 - 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/D9PS2YM0OS8H.D27X2G7ZEQRS@b1-systems.de Author(s): Robin Haberkorn
2 parents e050af2 + 28440c2 commit 5400f39

File tree

7 files changed

+254
-52
lines changed

7 files changed

+254
-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: 141 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);
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+
{
51159
text *result;
52-
text *paramstr;
53-
const char **params;
54160
PgXmlErrorContext *xmlerrcxt;
55161
volatile xsltStylesheetPtr stylesheet = NULL;
56162
volatile xmlDocPtr doctree = NULL;
@@ -61,48 +167,48 @@ xslt_process(PG_FUNCTION_ARGS)
61167
xmlChar *resstr = NULL;
62168
int reslen = 0;
63169

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

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

79184
PG_TRY();
80185
{
81186
xmlDocPtr ssdoc;
82187
bool xslt_sec_prefs_error;
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. It's important to set an "URL", so libxslt includes
191+
* line numbers in error messages (cf. xsltPrintErrorContext()).
192+
*/
193+
doctree = xml_parse(doct, XMLOPTION_DOCUMENT, true,
194+
GetDatabaseEncoding(), NULL, NULL, NULL);
88195

89-
if (doctree == NULL)
196+
if (doctree == NULL || pg_xml_error_occurred(xmlerrcxt))
90197
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
91198
"error parsing XML document");
92199

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

98-
if (ssdoc == NULL)
204+
if (ssdoc == NULL || pg_xml_error_occurred(xmlerrcxt))
99205
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
100206
"error parsing stylesheet as XML document");
101207

102208
/* After this call we need not free ssdoc separately */
103209
stylesheet = xsltParseStylesheetDoc(ssdoc);
104210

105-
if (stylesheet == NULL)
211+
if (stylesheet == NULL || pg_xml_error_occurred(xmlerrcxt))
106212
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
107213
"failed to parse stylesheet");
108214

@@ -137,11 +243,18 @@ xslt_process(PG_FUNCTION_ARGS)
137243
restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
138244
NULL, NULL, xslt_ctxt);
139245

140-
if (restree == NULL)
246+
if (restree == NULL || pg_xml_error_occurred(xmlerrcxt))
141247
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
142248
"failed to apply stylesheet");
143249

250+
/*
251+
* FIXME: There is probably a function to convert the string to the
252+
* correct database encoding.
253+
*/
144254
resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet);
255+
if (resstat < 0 || pg_xml_error_occurred(xmlerrcxt))
256+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
257+
"failed to save result to string");
145258
}
146259
PG_CATCH();
147260
{
@@ -157,6 +270,7 @@ xslt_process(PG_FUNCTION_ARGS)
157270
xmlFreeDoc(doctree);
158271
xsltCleanupGlobals();
159272

273+
xsltSetGenericErrorFunc(saved_errcxt, saved_errfunc);
160274
pg_xml_done(xmlerrcxt, true);
161275

162276
PG_RE_THROW();
@@ -170,29 +284,17 @@ xslt_process(PG_FUNCTION_ARGS)
170284
xmlFreeDoc(doctree);
171285
xsltCleanupGlobals();
172286

287+
xsltSetGenericErrorFunc(saved_errcxt, saved_errfunc);
173288
pg_xml_done(xmlerrcxt, false);
174289

175-
/* XXX this is pretty dubious, really ought to throw error instead */
176-
if (resstat < 0)
177-
PG_RETURN_NULL();
178-
179290
result = cstring_to_text_with_len((char *) resstr, reslen);
180291

181292
if (resstr)
182293
xmlFree(resstr);
183294

184-
PG_RETURN_TEXT_P(result);
185-
#else /* !USE_LIBXSLT */
186-
187-
ereport(ERROR,
188-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
189-
errmsg("xslt_process() is not available without libxslt")));
190-
PG_RETURN_NULL();
191-
#endif /* USE_LIBXSLT */
295+
return result;
192296
}
193297

194-
#ifdef USE_LIBXSLT
195-
196298
static const char **
197299
parse_params(text *paramstr)
198300
{

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)