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

Commit 7bf93b7

Browse files
Robin HaberkornCommitfest Bot
Robin Haberkorn
authored and
Commitfest Bot
committed
contrib/xml2: xslt_process() now reports XSLT-related error details
* It sets (and restores) the libxslt error handler (xsltSetGenericErrorFunc()). Since it only supports old-school "generic" error handlers, which are no longer used in PG's libxml code, we reintroduced a "generic" error handler xml_generic_error_handler() in xml.c. * The alternative would have been to expose PgXmlErrorContext in xml.h, so we could implement a generic handler in xslt_proc.c. This is obviously not desirable, as it would depend on USE_LIBXML. * No longer use the PG_XML_STRICTNESS_LEGACY for error handling, but query the error_occurred-flag via pg_xml_error_occurred(). The existance of this getter is another hint, that we are not supposed to expose PgXmlErrorContext in xml.h. * This change means that xslt_process() now reports not only details about XML parsing errors, but XSLT-schema deviations and missing stylesheet parameters as well. * The XSLT error messages now also contain line numbers. For that to work, we had to set a dummy "SQL" URL when parsing XML strings. This is important, since xsltPrintErrorContext() includes line numbers only if an URL is set. * The xsltSaveResultToString() error handling has been removed. It can practically only fail in OOM situations and there is no reason to handle them any different than with the other libxslt functions. * Updated test suite and added test case for detecting missing stylesheet parameters. This was initially reported here but has obviously been fixed in the meantime: https://www.postgresql.org/message-id/4C5ECEF9.3030806%40mlfowler.com
1 parent e050af2 commit 7bf93b7

File tree

5 files changed

+83
-12
lines changed

5 files changed

+83
-12
lines changed

contrib/xml2/expected/xml2.out

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,20 @@ $$<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.

contrib/xml2/sql/xml2.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,11 @@ $$<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;

contrib/xml2/xslt_proc.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ xslt_process(PG_FUNCTION_ARGS)
6161
xmlChar *resstr = NULL;
6262
int reslen = 0;
6363

64+
/* the previous libxslt error context */
65+
xmlGenericErrorFunc saved_errfunc;
66+
void *saved_errcxt;
67+
6468
if (fcinfo->nargs == 3)
6569
{
6670
paramstr = PG_GETARG_TEXT_PP(2);
@@ -74,35 +78,45 @@ xslt_process(PG_FUNCTION_ARGS)
7478
}
7579

7680
/* Setup parser */
77-
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
81+
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_ALL);
82+
83+
/*
84+
* Save the previous libxslt error context.
85+
*/
86+
saved_errfunc = xsltGenericError;
87+
saved_errcxt = xsltGenericErrorContext;
88+
xsltSetGenericErrorFunc(xmlerrcxt, xml_generic_error_handler);
7889

7990
PG_TRY();
8091
{
8192
xmlDocPtr ssdoc;
8293
bool xslt_sec_prefs_error;
8394

84-
/* Parse document */
95+
/*
96+
* Parse document. It's important to set an "URL", so libxslt includes
97+
* line numbers in error messages (cf. xsltPrintErrorContext()).
98+
*/
8599
doctree = xmlReadMemory((char *) VARDATA_ANY(doct),
86-
VARSIZE_ANY_EXHDR(doct), NULL, NULL,
100+
VARSIZE_ANY_EXHDR(doct), "SQL", NULL,
87101
XML_PARSE_NOENT);
88102

89-
if (doctree == NULL)
103+
if (doctree == NULL || pg_xml_error_occurred(xmlerrcxt))
90104
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
91105
"error parsing XML document");
92106

93107
/* Same for stylesheet */
94108
ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet),
95-
VARSIZE_ANY_EXHDR(ssheet), NULL, NULL,
109+
VARSIZE_ANY_EXHDR(ssheet), "SQL", NULL,
96110
XML_PARSE_NOENT);
97111

98-
if (ssdoc == NULL)
112+
if (ssdoc == NULL || pg_xml_error_occurred(xmlerrcxt))
99113
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
100114
"error parsing stylesheet as XML document");
101115

102116
/* After this call we need not free ssdoc separately */
103117
stylesheet = xsltParseStylesheetDoc(ssdoc);
104118

105-
if (stylesheet == NULL)
119+
if (stylesheet == NULL || pg_xml_error_occurred(xmlerrcxt))
106120
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
107121
"failed to parse stylesheet");
108122

@@ -137,11 +151,14 @@ xslt_process(PG_FUNCTION_ARGS)
137151
restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
138152
NULL, NULL, xslt_ctxt);
139153

140-
if (restree == NULL)
154+
if (restree == NULL || pg_xml_error_occurred(xmlerrcxt))
141155
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
142156
"failed to apply stylesheet");
143157

144158
resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet);
159+
if (resstat < 0 || pg_xml_error_occurred(xmlerrcxt))
160+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
161+
"failed to save result to string");
145162
}
146163
PG_CATCH();
147164
{
@@ -157,6 +174,7 @@ xslt_process(PG_FUNCTION_ARGS)
157174
xmlFreeDoc(doctree);
158175
xsltCleanupGlobals();
159176

177+
xsltSetGenericErrorFunc(saved_errcxt, saved_errfunc);
160178
pg_xml_done(xmlerrcxt, true);
161179

162180
PG_RE_THROW();
@@ -170,12 +188,9 @@ xslt_process(PG_FUNCTION_ARGS)
170188
xmlFreeDoc(doctree);
171189
xsltCleanupGlobals();
172190

191+
xsltSetGenericErrorFunc(saved_errcxt, saved_errfunc);
173192
pg_xml_done(xmlerrcxt, false);
174193

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

181196
if (resstr)

src/backend/utils/adt/xml.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,6 +2091,35 @@ xml_errsave(Node *escontext, PgXmlErrorContext *errcxt,
20912091
detail ? errdetail_internal("%s", detail) : 0));
20922092
}
20932093

2094+
/*
2095+
* Generic error handler for libxml errors and warnings.
2096+
* This is not used by this module, but may be useful for
2097+
* libxml-based libraries like libxslt, which do not support
2098+
* structured error handlers.
2099+
*/
2100+
void
2101+
xml_generic_error_handler(void *data, const char *msg,...)
2102+
{
2103+
PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data;
2104+
va_list ap;
2105+
2106+
/*
2107+
* Defend against someone passing us a bogus context struct.
2108+
*
2109+
* We force a backend exit if this check fails because longjmp'ing out of
2110+
* libxslt would likely render it unsafe to use further.
2111+
*/
2112+
if (xmlerrcxt->magic != ERRCXT_MAGIC)
2113+
elog(FATAL, "xml_generic_error_handler called with invalid PgXmlErrorContext");
2114+
2115+
appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
2116+
va_start(ap, msg);
2117+
appendStringInfoVA(&xmlerrcxt->err_buf, msg, ap);
2118+
va_end(ap);
2119+
2120+
/* Get rid of any trailing newlines in errorBuf */
2121+
chopStringInfoNewlines(&xmlerrcxt->err_buf);
2122+
}
20942123

20952124
/*
20962125
* Error handler for libxml errors and warnings

src/include/utils/xml.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ extern void pg_xml_init_library(void);
6666
extern PgXmlErrorContext *pg_xml_init(PgXmlStrictness strictness);
6767
extern void pg_xml_done(PgXmlErrorContext *errcxt, bool isError);
6868
extern bool pg_xml_error_occurred(PgXmlErrorContext *errcxt);
69+
extern void xml_generic_error_handler(void *data, const char *msg,...)
70+
pg_attribute_printf(2, 3);
6971
extern void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode,
7072
const char *msg);
7173

0 commit comments

Comments
 (0)