mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
When our XML-handling modules were first written, the SQL standard lacked any error codes that were particularly intended for XML error conditions. Unsurprisingly, this led to some rather random choices of errcodes in those modules. Now the standard has a whole SQLSTATE class, "Class 10 - XQuery Error", with a reasonably large selection of relevant-looking errcodes. In this patch I've chosen one fairly generic code defined by the standard, 10608 = invalid_argument_for_xquery, and used it where it seemed appropriate. I've also made an effort to replace ERRCODE_INTERNAL_ERROR everywhere it was not clearly reporting a coding problem; in particular, many of the existing uses look like they can fairly be reported as ERRCODE_OUT_OF_MEMORY. It might be interesting to try to map libxml2's error codes into the standard's new collection, but I've not undertaken that here. Discussion: https://postgr.es/m/417250.1726341268@sss.pgh.pa.us
259 lines
6.0 KiB
C
259 lines
6.0 KiB
C
/*
|
|
* contrib/xml2/xslt_proc.c
|
|
*
|
|
* XSLT processing functions (requiring libxslt)
|
|
*
|
|
* John Gray, for Torchbox 2003-04-01
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "executor/spi.h"
|
|
#include "fmgr.h"
|
|
#include "funcapi.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/xml.h"
|
|
|
|
#ifdef USE_LIBXSLT
|
|
|
|
/* libxml includes */
|
|
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/xmlmemory.h>
|
|
|
|
/* libxslt includes */
|
|
|
|
#include <libxslt/xslt.h>
|
|
#include <libxslt/xsltInternals.h>
|
|
#include <libxslt/security.h>
|
|
#include <libxslt/transform.h>
|
|
#include <libxslt/xsltutils.h>
|
|
#endif /* USE_LIBXSLT */
|
|
|
|
|
|
#ifdef USE_LIBXSLT
|
|
|
|
/* declarations to come from xpath.c */
|
|
extern PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness);
|
|
|
|
/* local defs */
|
|
static const char **parse_params(text *paramstr);
|
|
#endif /* USE_LIBXSLT */
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(xslt_process);
|
|
|
|
Datum
|
|
xslt_process(PG_FUNCTION_ARGS)
|
|
{
|
|
#ifdef USE_LIBXSLT
|
|
|
|
text *doct = PG_GETARG_TEXT_PP(0);
|
|
text *ssheet = PG_GETARG_TEXT_PP(1);
|
|
text *result;
|
|
text *paramstr;
|
|
const char **params;
|
|
PgXmlErrorContext *xmlerrcxt;
|
|
volatile xsltStylesheetPtr stylesheet = NULL;
|
|
volatile xmlDocPtr doctree = NULL;
|
|
volatile xmlDocPtr restree = NULL;
|
|
volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL;
|
|
volatile xsltTransformContextPtr xslt_ctxt = NULL;
|
|
volatile int resstat = -1;
|
|
xmlChar *resstr = NULL;
|
|
int reslen = 0;
|
|
|
|
if (fcinfo->nargs == 3)
|
|
{
|
|
paramstr = PG_GETARG_TEXT_PP(2);
|
|
params = parse_params(paramstr);
|
|
}
|
|
else
|
|
{
|
|
/* No parameters */
|
|
params = (const char **) palloc(sizeof(char *));
|
|
params[0] = NULL;
|
|
}
|
|
|
|
/* Setup parser */
|
|
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
|
|
|
|
PG_TRY();
|
|
{
|
|
xmlDocPtr ssdoc;
|
|
bool xslt_sec_prefs_error;
|
|
|
|
/* Parse document */
|
|
doctree = xmlReadMemory((char *) VARDATA_ANY(doct),
|
|
VARSIZE_ANY_EXHDR(doct), NULL, NULL,
|
|
XML_PARSE_NOENT);
|
|
|
|
if (doctree == NULL)
|
|
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
|
|
"error parsing XML document");
|
|
|
|
/* Same for stylesheet */
|
|
ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet),
|
|
VARSIZE_ANY_EXHDR(ssheet), NULL, NULL,
|
|
XML_PARSE_NOENT);
|
|
|
|
if (ssdoc == NULL)
|
|
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
|
|
"error parsing stylesheet as XML document");
|
|
|
|
/* After this call we need not free ssdoc separately */
|
|
stylesheet = xsltParseStylesheetDoc(ssdoc);
|
|
|
|
if (stylesheet == NULL)
|
|
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
|
|
"failed to parse stylesheet");
|
|
|
|
xslt_ctxt = xsltNewTransformContext(stylesheet, doctree);
|
|
|
|
xslt_sec_prefs_error = false;
|
|
if ((xslt_sec_prefs = xsltNewSecurityPrefs()) == NULL)
|
|
xslt_sec_prefs_error = true;
|
|
|
|
if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_FILE,
|
|
xsltSecurityForbid) != 0)
|
|
xslt_sec_prefs_error = true;
|
|
if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE,
|
|
xsltSecurityForbid) != 0)
|
|
xslt_sec_prefs_error = true;
|
|
if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
|
|
xsltSecurityForbid) != 0)
|
|
xslt_sec_prefs_error = true;
|
|
if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK,
|
|
xsltSecurityForbid) != 0)
|
|
xslt_sec_prefs_error = true;
|
|
if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK,
|
|
xsltSecurityForbid) != 0)
|
|
xslt_sec_prefs_error = true;
|
|
if (xsltSetCtxtSecurityPrefs(xslt_sec_prefs, xslt_ctxt) != 0)
|
|
xslt_sec_prefs_error = true;
|
|
|
|
if (xslt_sec_prefs_error)
|
|
ereport(ERROR,
|
|
(errmsg("could not set libxslt security preferences")));
|
|
|
|
restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
|
|
NULL, NULL, xslt_ctxt);
|
|
|
|
if (restree == NULL)
|
|
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
|
|
"failed to apply stylesheet");
|
|
|
|
resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet);
|
|
}
|
|
PG_CATCH();
|
|
{
|
|
if (restree != NULL)
|
|
xmlFreeDoc(restree);
|
|
if (xslt_ctxt != NULL)
|
|
xsltFreeTransformContext(xslt_ctxt);
|
|
if (xslt_sec_prefs != NULL)
|
|
xsltFreeSecurityPrefs(xslt_sec_prefs);
|
|
if (stylesheet != NULL)
|
|
xsltFreeStylesheet(stylesheet);
|
|
if (doctree != NULL)
|
|
xmlFreeDoc(doctree);
|
|
xsltCleanupGlobals();
|
|
|
|
pg_xml_done(xmlerrcxt, true);
|
|
|
|
PG_RE_THROW();
|
|
}
|
|
PG_END_TRY();
|
|
|
|
xmlFreeDoc(restree);
|
|
xsltFreeTransformContext(xslt_ctxt);
|
|
xsltFreeSecurityPrefs(xslt_sec_prefs);
|
|
xsltFreeStylesheet(stylesheet);
|
|
xmlFreeDoc(doctree);
|
|
xsltCleanupGlobals();
|
|
|
|
pg_xml_done(xmlerrcxt, false);
|
|
|
|
/* XXX this is pretty dubious, really ought to throw error instead */
|
|
if (resstat < 0)
|
|
PG_RETURN_NULL();
|
|
|
|
result = cstring_to_text_with_len((char *) resstr, reslen);
|
|
|
|
if (resstr)
|
|
xmlFree(resstr);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
#else /* !USE_LIBXSLT */
|
|
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("xslt_process() is not available without libxslt")));
|
|
PG_RETURN_NULL();
|
|
#endif /* USE_LIBXSLT */
|
|
}
|
|
|
|
#ifdef USE_LIBXSLT
|
|
|
|
static const char **
|
|
parse_params(text *paramstr)
|
|
{
|
|
char *pos;
|
|
char *pstr;
|
|
char *nvsep = "=";
|
|
char *itsep = ",";
|
|
const char **params;
|
|
int max_params;
|
|
int nparams;
|
|
|
|
pstr = text_to_cstring(paramstr);
|
|
|
|
max_params = 20; /* must be even! */
|
|
params = (const char **) palloc((max_params + 1) * sizeof(char *));
|
|
nparams = 0;
|
|
|
|
pos = pstr;
|
|
|
|
while (*pos != '\0')
|
|
{
|
|
if (nparams >= max_params)
|
|
{
|
|
max_params *= 2;
|
|
params = (const char **) repalloc(params,
|
|
(max_params + 1) * sizeof(char *));
|
|
}
|
|
params[nparams++] = pos;
|
|
pos = strstr(pos, nvsep);
|
|
if (pos != NULL)
|
|
{
|
|
*pos = '\0';
|
|
pos++;
|
|
}
|
|
else
|
|
{
|
|
/* No equal sign, so ignore this "parameter" */
|
|
nparams--;
|
|
break;
|
|
}
|
|
|
|
/* since max_params is even, we still have nparams < max_params */
|
|
params[nparams++] = pos;
|
|
pos = strstr(pos, itsep);
|
|
if (pos != NULL)
|
|
{
|
|
*pos = '\0';
|
|
pos++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
/* Add the terminator marker; we left room for it in the palloc's */
|
|
params[nparams] = NULL;
|
|
|
|
return params;
|
|
}
|
|
|
|
#endif /* USE_LIBXSLT */
|