diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 44c600e..556b5a1 100644
*** a/contrib/xml2/xpath.c
--- b/contrib/xml2/xpath.c
*************** Datum xpath_table(PG_FUNCTION_ARGS);
*** 38,44 ****
/* exported for use by xslt_proc.c */
! void pgxml_parser_init(void);
/* workspace for pgxml_xpath() */
--- 38,44 ----
/* exported for use by xslt_proc.c */
! void pgxml_parser_init(XmlPurposeType purpose);
/* workspace for pgxml_xpath() */
*************** static void cleanup_workspace(xpath_work
*** 70,79 ****
* Initialize for xml parsing.
*/
void
! pgxml_parser_init(void)
{
/* Set up error handling (we share the core's error handler) */
! pg_xml_init();
/* Initialize libxml */
xmlInitParser();
--- 70,79 ----
* Initialize for xml parsing.
*/
void
! pgxml_parser_init(XmlPurposeType purpose)
{
/* Set up error handling (we share the core's error handler) */
! pg_xml_init(purpose);
/* Initialize libxml */
xmlInitParser();
*************** xml_is_well_formed(PG_FUNCTION_ARGS)
*** 100,113 ****
text *t = PG_GETARG_TEXT_P(0); /* document buffer */
int32 docsize = VARSIZE(t) - VARHDRSZ;
xmlDocPtr doctree;
! pgxml_parser_init();
doctree = xmlParseMemory((char *) VARDATA(t), docsize);
! if (doctree == NULL)
! PG_RETURN_BOOL(false); /* i.e. not well-formed */
xmlFreeDoc(doctree);
! PG_RETURN_BOOL(true);
}
--- 100,116 ----
text *t = PG_GETARG_TEXT_P(0); /* document buffer */
int32 docsize = VARSIZE(t) - VARHDRSZ;
xmlDocPtr doctree;
+ bool result;
! pgxml_parser_init(XML_PURPOSE_LEGACY);
doctree = xmlParseMemory((char *) VARDATA(t), docsize);
! result = (doctree != NULL);
xmlFreeDoc(doctree);
!
! pg_xml_done();
!
! PG_RETURN_BOOL(result);
}
*************** pgxml_xpath(text *document, xmlChar *xpa
*** 406,412 ****
workspace->ctxt = NULL;
workspace->res = NULL;
! pgxml_parser_init();
workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize);
if (workspace->doctree == NULL)
--- 409,415 ----
workspace->ctxt = NULL;
workspace->res = NULL;
! pgxml_parser_init(XML_PURPOSE_LEGACY);
workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize);
if (workspace->doctree == NULL)
*************** pgxml_xpath(text *document, xmlChar *xpa
*** 420,425 ****
--- 423,429 ----
if (comppath == NULL)
{
cleanup_workspace(workspace);
+ pg_xml_done();
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"XPath Syntax Error");
}
*************** pgxml_xpath(text *document, xmlChar *xpa
*** 433,438 ****
--- 437,444 ----
if (res == NULL)
cleanup_workspace(workspace);
+ pg_xml_done();
+
return res;
}
*************** xpath_table(PG_FUNCTION_ARGS)
*** 659,665 ****
* Setup the parser. This should happen after we are done evaluating the
* query, in case it calls functions that set up libxml differently.
*/
! pgxml_parser_init();
/* For each row i.e. document returned from SPI */
for (i = 0; i < proc; i++)
--- 665,671 ----
* Setup the parser. This should happen after we are done evaluating the
* query, in case it calls functions that set up libxml differently.
*/
! pgxml_parser_init(XML_PURPOSE_LEGACY);
/* For each row i.e. document returned from SPI */
for (i = 0; i < proc; i++)
*************** xpath_table(PG_FUNCTION_ARGS)
*** 720,725 ****
--- 726,732 ----
if (comppath == NULL)
{
xmlFreeDoc(doctree);
+ pg_xml_done();
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"XPath Syntax Error");
}
*************** xpath_table(PG_FUNCTION_ARGS)
*** 784,789 ****
--- 791,798 ----
pfree(xmldoc);
}
+ pg_xml_done();
+
tuplestore_donestoring(tupstore);
SPI_finish();
diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c
index f8f7d72..b71cafe 100644
*** a/contrib/xml2/xslt_proc.c
--- b/contrib/xml2/xslt_proc.c
*************** Datum xslt_process(PG_FUNCTION_ARGS);
*** 38,44 ****
#ifdef USE_LIBXSLT
/* declarations to come from xpath.c */
! extern void pgxml_parser_init(void);
/* local defs */
static const char **parse_params(text *paramstr);
--- 38,44 ----
#ifdef USE_LIBXSLT
/* declarations to come from xpath.c */
! extern void pgxml_parser_init(XmlPurposeType purpose);
/* local defs */
static const char **parse_params(text *paramstr);
*************** xslt_process(PG_FUNCTION_ARGS)
*** 77,83 ****
}
/* Setup parser */
! pgxml_parser_init();
/* Check to see if document is a file or a literal */
--- 77,83 ----
}
/* Setup parser */
! pgxml_parser_init(XML_PURPOSE_LEGACY);
/* Check to see if document is a file or a literal */
*************** xslt_process(PG_FUNCTION_ARGS)
*** 86,94 ****
else
doctree = xmlParseFile(text_to_cstring(doct));
! if (doctree == NULL)
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"error parsing XML document");
/* Same for stylesheet */
if (VARDATA(ssheet)[0] == '<')
--- 86,96 ----
else
doctree = xmlParseFile(text_to_cstring(doct));
! if (doctree == NULL) {
! pg_xml_done();
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"error parsing XML document");
+ }
/* Same for stylesheet */
if (VARDATA(ssheet)[0] == '<')
*************** xslt_process(PG_FUNCTION_ARGS)
*** 98,103 ****
--- 100,106 ----
if (ssdoc == NULL)
{
xmlFreeDoc(doctree);
+ pg_xml_done();
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"error parsing stylesheet as XML document");
}
*************** xslt_process(PG_FUNCTION_ARGS)
*** 112,117 ****
--- 115,121 ----
{
xmlFreeDoc(doctree);
xsltCleanupGlobals();
+ pg_xml_done();
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"failed to parse stylesheet");
}
*************** xslt_process(PG_FUNCTION_ARGS)
*** 125,130 ****
--- 129,136 ----
xsltCleanupGlobals();
+ pg_xml_done();
+
if (resstat < 0)
PG_RETURN_NULL();
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 702b9e3..3922bb9 100644
*** a/src/backend/utils/adt/xml.c
--- b/src/backend/utils/adt/xml.c
***************
*** 82,92 ****
int xmlbinary;
int xmloption;
#ifdef USE_LIBXML
! static StringInfo xml_err_buf = NULL;
! static void xml_errorHandler(void *ctxt, const char *msg,...);
static void xml_ereport_by_code(int level, int sqlcode,
const char *msg, int errcode);
--- 82,102 ----
int xmlbinary;
int xmloption;
+ /* Flag indicating whether libxml reported an error */
+ bool xml_error_occured = false;
+
#ifdef USE_LIBXML
! static bool xml_error_initialized = false;
! static xmlStructuredErrorFunc xml_structuredErrorFunc_saved = NULL;
! static void* xml_structuredErrorContext_saved = NULL;
! static XmlPurposeType xml_purpose = XML_PURPOSE_NONE;
! static StringInfo xml_error_detail_buf = NULL;
!
! static void xml_errorHandler(void* data, xmlErrorPtr error);
! static void appendStringInfoLineSeparator(StringInfo str);
! static void chopStringInfoNewlines(StringInfo str);
static void xml_ereport_by_code(int level, int sqlcode,
const char *msg, int errcode);
*************** xmlelement(XmlExprState *xmlExpr, ExprCo
*** 597,614 ****
}
/* now safe to run libxml */
! pg_xml_init();
PG_TRY();
{
buf = xmlBufferCreate();
! if (!buf)
! xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlBuffer");
writer = xmlNewTextWriterMemory(buf, 0);
! if (!writer)
! xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlTextWriter");
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
--- 607,622 ----
}
/* now safe to run libxml */
! pg_xml_init(XML_PURPOSE_OTHER);
PG_TRY();
{
buf = xmlBufferCreate();
! XML_REPORT_ERROR(buf != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlBuffer");
writer = xmlNewTextWriterMemory(buf, 0);
! XML_REPORT_ERROR(writer != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlTextWriter");
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
*************** xmlelement(XmlExprState *xmlExpr, ExprCo
*** 644,655 ****
--- 652,668 ----
xmlFreeTextWriter(writer);
if (buf)
xmlBufferFree(buf);
+
+ pg_xml_done();
+
PG_RE_THROW();
}
PG_END_TRY();
xmlBufferFree(buf);
+ pg_xml_done();
+
return result;
#else
NO_XML_SUPPORT();
*************** xml_is_document(xmltype *arg)
*** 848,856 ****
* This should be called by each function that is about to use libxml
* facilities. It has two responsibilities: verify compatibility with the
* loaded libxml version (done on first call in a session) and establish
! * or re-establish our libxml error handler. The latter needs to be done
! * anytime we might have passed control to add-on modules (eg libperl) which
! * might have set their own error handler for libxml.
*
* This is exported for use by contrib/xml2, as well as other code that might
* wish to share use of this module's libxml error handler.
--- 861,871 ----
* This should be called by each function that is about to use libxml
* facilities. It has two responsibilities: verify compatibility with the
* loaded libxml version (done on first call in a session) and establish
! * our libxml error handler. The purpose determines which errors are
! * reported and which are ignored. If the purpose is XML_PURPOSE_NONE,
! * error handling is skipped entirely. If the purpose is any other value,
! * pg_xml_done() *must* be called after the caller is done using libxml
! * to restore the original error handler.
*
* This is exported for use by contrib/xml2, as well as other code that might
* wish to share use of this module's libxml error handler.
*************** xml_is_document(xmltype *arg)
*** 859,868 ****
* check)
*/
void
! pg_xml_init(void)
{
static bool first_time = true;
!
if (first_time)
{
/* Stuff we need do only once per session */
--- 874,883 ----
* check)
*/
void
! pg_xml_init(XmlPurposeType purpose)
{
static bool first_time = true;
!
if (first_time)
{
/* Stuff we need do only once per session */
*************** pg_xml_init(void)
*** 880,890 ****
/* create error buffer in permanent context */
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
! xml_err_buf = makeStringInfo();
MemoryContextSwitchTo(oldcontext);
!
! /* Now that xml_err_buf exists, safe to call xml_errorHandler */
! xmlSetGenericErrorFunc(NULL, xml_errorHandler);
#ifdef USE_LIBXMLCONTEXT
/* Set up memory allocation our way, too */
--- 895,903 ----
/* create error buffer in permanent context */
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
! xml_error_detail_buf = makeStringInfo();
MemoryContextSwitchTo(oldcontext);
! xml_error_occured = false;
#ifdef USE_LIBXMLCONTEXT
/* Set up memory allocation our way, too */
*************** pg_xml_init(void)
*** 896,916 ****
first_time = false;
}
! else
! {
! /* Reset pre-existing buffer to empty */
! Assert(xml_err_buf != NULL);
! resetStringInfo(xml_err_buf);
! /*
! * We re-establish the error callback function every time. This makes
! * it safe for other subsystems (PL/Perl, say) to also use libxml with
! * their own callbacks ... so long as they likewise set up the
! * callbacks on every use. It's cheap enough to not be worth worrying
! * about, anyway.
! */
! xmlSetGenericErrorFunc(NULL, xml_errorHandler);
}
}
--- 909,983 ----
first_time = false;
}
!
! /* Purpose XML_PURPOSE_NONE tells us to only setup libxml, not
! * the error handler. pg_xml_done() must *not* be called in this
! * case!
! */
! if (purpose == XML_PURPOSE_NONE)
! return;
!
! /* Imbalanced calls of pg_xml_init() and pg_xml_done() */
! Assert(!xml_error_initialized);
! Assert(xml_purpose == XML_PURPOSE_NONE);
!
! /* Set purpose */
! xml_purpose = purpose;
!
! /* Reset pre-existing buffer to empty */
! Assert(xml_error_detail_buf != NULL);
! resetStringInfo(xml_error_detail_buf);
! xml_error_occured = false;
!
! /* Save original error handler and install ours */
! xml_structuredErrorFunc_saved = xmlStructuredError;
! xml_structuredErrorContext_saved = xmlStructuredErrorContext;
! xmlSetStructuredErrorFunc(NULL, xml_errorHandler);
! xml_error_initialized = true;
! }
!
!
! /*
! * pg_xml_done --- restore libxml state after pg_xml_init().
! *
! * This must be called if pg_xml_init() was called with a
! * purpose other than XML_PURPOSE_NONE. Resets libxml's global state
! * (i.e. the structured error handler) to the original state.
! *
! * It is OK to call xml_ereport() after pg_xml_done() -
! * pg_xml_done() leaves xml_error_detail_buf as it is.
! */
! void
! pg_xml_done(void)
! {
! /* Imbalanced calls of pg_xml_init() and pg_xml_done(). */
! Assert(xml_error_initialized);
! Assert(xml_purpose != XML_PURPOSE_NONE);
!
! xmlSetStructuredErrorFunc(xml_structuredErrorContext_saved,
! xml_structuredErrorFunc_saved);
! xml_purpose = XML_PURPOSE_NONE;
! xml_error_initialized = false;
! }
!
!
!
! /*
! * pg_xml_erroroccurred() --- Test and reset the error flag.
! *
! * Returns true if an libxml error occurred after the last call of
! * this function.
! */
! bool
! pg_xml_erroroccurred(void)
! {
! if (xml_error_occured) {
! xml_error_occured = false;
! return true;
}
+
+ return false;
}
*************** parse_xml_decl(const xmlChar *str, size_
*** 969,975 ****
int utf8char;
int utf8len;
! pg_xml_init();
/* Initialize output arguments to "not present" */
if (version)
--- 1036,1046 ----
int utf8char;
int utf8len;
! /* Only initialize libxml. We don't need error handling here,
! * but we do need to make sure libxml is initialized before
! * calling any of its functions
! */
! pg_xml_init(XML_PURPOSE_NONE);
/* Initialize output arguments to "not present" */
if (version)
*************** static bool
*** 1123,1130 ****
print_xml_decl(StringInfo buf, const xmlChar *version,
pg_enc encoding, int standalone)
{
- pg_xml_init(); /* why is this here? */
-
if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
|| (encoding && encoding != PG_UTF8)
|| standalone != -1)
--- 1194,1199 ----
*************** xml_parse(text *data, XmlOptionType xmlo
*** 1175,1182 ****
int32 len;
xmlChar *string;
xmlChar *utf8string;
! xmlParserCtxtPtr ctxt;
! xmlDocPtr doc;
len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
string = xml_text2xmlChar(data);
--- 1244,1251 ----
int32 len;
xmlChar *string;
xmlChar *utf8string;
! xmlParserCtxtPtr ctxt = NULL;
! xmlDocPtr doc = NULL;
len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
string = xml_text2xmlChar(data);
*************** xml_parse(text *data, XmlOptionType xmlo
*** 1187,1203 ****
PG_UTF8);
/* Start up libxml and its parser (no-ops if already done) */
! pg_xml_init();
xmlInitParser();
- ctxt = xmlNewParserCtxt();
- if (ctxt == NULL)
- xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
- "could not allocate parser context");
-
/* Use a TRY block to ensure the ctxt is released */
PG_TRY();
{
if (xmloption_arg == XMLOPTION_DOCUMENT)
{
/*
--- 1256,1271 ----
PG_UTF8);
/* Start up libxml and its parser (no-ops if already done) */
! pg_xml_init(XML_PURPOSE_WELLFORMED);
xmlInitParser();
/* Use a TRY block to ensure the ctxt is released */
PG_TRY();
{
+ ctxt = xmlNewParserCtxt();
+ XML_REPORT_ERROR(ctxt != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
+ "could not allocate parser context");
+
if (xmloption_arg == XMLOPTION_DOCUMENT)
{
/*
*************** xml_parse(text *data, XmlOptionType xmlo
*** 1212,1220 ****
"UTF-8",
XML_PARSE_NOENT | XML_PARSE_DTDATTR
| (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
! if (doc == NULL)
! xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
! "invalid XML document");
}
else
{
--- 1280,1287 ----
"UTF-8",
XML_PARSE_NOENT | XML_PARSE_DTDATTR
| (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
! XML_REPORT_ERROR(doc != NULL, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
! "invalid XML document");
}
else
{
*************** xml_parse(text *data, XmlOptionType xmlo
*** 1237,1259 ****
res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
utf8string + count, NULL);
! if (res_code != 0)
! {
! xmlFreeDoc(doc);
! xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
! "invalid XML content");
! }
}
}
PG_CATCH();
{
! xmlFreeParserCtxt(ctxt);
PG_RE_THROW();
}
PG_END_TRY();
xmlFreeParserCtxt(ctxt);
!
return doc;
}
--- 1304,1330 ----
res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
utf8string + count, NULL);
! XML_REPORT_ERROR(res_code == 0, ERROR, ERRCODE_INVALID_XML_CONTENT,
! "invalid XML content");
}
}
PG_CATCH();
{
! if (doc != NULL)
! xmlFreeDoc(doc);
! if (ctxt != NULL)
! xmlFreeParserCtxt(ctxt);
!
! pg_xml_done();
!
PG_RE_THROW();
}
PG_END_TRY();
xmlFreeParserCtxt(ctxt);
!
! pg_xml_done();
!
return doc;
}
*************** void
*** 1337,1366 ****
xml_ereport(int level, int sqlcode, const char *msg)
{
char *detail;
!
/*
! * It might seem that we should just pass xml_err_buf->data directly to
! * errdetail. However, we want to clean out xml_err_buf before throwing
* error, in case there is another function using libxml further down the
* call stack.
*/
! if (xml_err_buf->len > 0)
{
! detail = pstrdup(xml_err_buf->data);
! resetStringInfo(xml_err_buf);
}
else
detail = NULL;
if (detail)
{
- size_t len;
-
- /* libxml error messages end in '\n'; get rid of it */
- len = strlen(detail);
- if (len > 0 && detail[len - 1] == '\n')
- detail[len - 1] = '\0';
-
ereport(level,
(errcode(sqlcode),
errmsg("%s", msg),
--- 1408,1430 ----
xml_ereport(int level, int sqlcode, const char *msg)
{
char *detail;
!
/*
! * It might seem that we should just pass xml_error_detail_buf->data directly to
! * errdetail. However, we want to clean out xml_error_detail_buf before throwing
* error, in case there is another function using libxml further down the
* call stack.
*/
! if (xml_error_detail_buf->len > 0)
{
! detail = pstrdup(xml_error_detail_buf->data);
! resetStringInfo(xml_error_detail_buf);
}
else
detail = NULL;
if (detail)
{
ereport(level,
(errcode(sqlcode),
errmsg("%s", msg),
*************** xml_ereport(int level, int sqlcode, cons
*** 1376,1402 ****
/*
! * Error handler for libxml error messages
*/
static void
! xml_errorHandler(void *ctxt, const char *msg,...)
{
! /* Append the formatted text to xml_err_buf */
! for (;;)
{
! va_list args;
! bool success;
- /* Try to format the data. */
- va_start(args, msg);
- success = appendStringInfoVA(xml_err_buf, msg, args);
- va_end(args);
! if (success)
break;
!
! /* Double the buffer size and try again. */
! enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen);
}
}
--- 1440,1566 ----
/*
! * Append a newline after removing all existing trailing newlines
*/
static void
! appendStringInfoLineSeparator(StringInfo str)
{
! chopStringInfoNewlines(str);
!
! if (str->len > 0)
! appendStringInfoChar(str, '\n');
! }
!
!
! /*
! * Remove all trailing newlines
! */
! static void
! chopStringInfoNewlines(StringInfo str)
! {
! while ((str->len > 0) &&
! (str->data[str->len-1] == '\n'))
{
! str->data[--str->len] = '\0';
! }
! }
! /*
! * Error handler for libxml errors and warnings
! */
! static void
! xml_errorHandler(void* data, xmlErrorPtr error)
! {
! /* Buffer to hold the error detail */
! StringInfo errorBuf = makeStringInfo();
!
! /* Get parser and input context */
! xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) error->ctxt;
! xmlParserInputPtr input = (ctxt != NULL) ? ctxt->input : NULL;
! xmlNodePtr node = error->node;
! const xmlChar* name = ((node != NULL) && (node->type == XML_ELEMENT_NODE)) ?
! node->name : NULL;
!
! switch (error->domain) {
! case XML_FROM_NONE:
! case XML_FROM_PARSER:
! case XML_FROM_MEMORY:
! case XML_FROM_IO:
! /* Accept regardless of the parsing purpose */
! break;
!
! default:
! /* Ignore during well-formedness check */
! if (xml_purpose == XML_PURPOSE_WELLFORMED)
! return;
break;
! }
!
! /* Append error message to xml_error_detail_buf */
! if ((error->domain == XML_FROM_PARSER) && (error->line > 0))
! appendStringInfo(errorBuf, "line %d: ", error->line);
! if (name != NULL)
! appendStringInfo(errorBuf, "element %s: ", name);
! appendStringInfo(errorBuf, "%s", error->message);
!
! /* Append context information to xml_error_detail_buf.
! * xmlParserPrintFileContext() uses the *generic* error handle to
! * write the context. Since we don't want to duplicate libxml
! * functionality here, we setup a generic error handler temporarily
! */
! if (input != NULL)
! {
! /* Save generic error func and setup the xml_error_detail_buf
! * appender as generic error func. This should work because
! * appendStringInfo() has essentially the same signature as
! * xmlGenericErrorFunc().
! */
! xmlGenericErrorFunc errFuncSaved = xmlGenericError;
! void* errCtxSaved = xmlGenericErrorContext;
! xmlSetGenericErrorFunc(errorBuf, (xmlGenericErrorFunc)appendStringInfo);
!
! /* Generic context information */
! appendStringInfoLineSeparator(errorBuf);
! xmlParserPrintFileContext(input);
!
! /* Restore generic error func */
! xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved);
! }
!
! chopStringInfoNewlines(errorBuf);
!
! /* Legacy error handling. The error flag is never set. Exists because
! * the xml2 contrib module uses our error-handling infrastructure, but
! * we don't want to change its behaviour since it's deprecated anyway
! */
! if (xml_purpose == XML_PURPOSE_LEGACY)
! {
! appendStringInfoLineSeparator(xml_error_detail_buf);
! appendStringInfoString(xml_error_detail_buf, errorBuf->data);
! return;
! }
!
! /* We don't want to ereport() here because that'd probably leave
! * libxml in an inconsistent state. Instead, we remember the
! * error and ereport() from xml_ereport().
! *
! * Warnings and notices are reported immediatly since they don't cause a
! * longjmp() out of libxml.
! */
! if (error->level >= XML_ERR_ERROR)
! {
! appendStringInfoLineSeparator(xml_error_detail_buf);
! appendStringInfoString(xml_error_detail_buf, errorBuf->data);
! xml_error_occured = true;
! }
! else if (error->level >= XML_ERR_WARNING)
! {
! ereport(WARNING, (errmsg("%s", errorBuf->data)));
! }
! else
! {
! ereport(NOTICE, (errmsg("%s", errorBuf->data)));
}
}
*************** map_sql_value_to_xml_value(Datum value,
*** 1756,1773 ****
xmlTextWriterPtr writer = NULL;
char *result;
! pg_xml_init();
PG_TRY();
{
buf = xmlBufferCreate();
! if (!buf)
! xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlBuffer");
writer = xmlNewTextWriterMemory(buf, 0);
! if (!writer)
! xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlTextWriter");
if (xmlbinary == XMLBINARY_BASE64)
xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
--- 1920,1935 ----
xmlTextWriterPtr writer = NULL;
char *result;
! pg_xml_init(XML_PURPOSE_OTHER);
PG_TRY();
{
buf = xmlBufferCreate();
! XML_REPORT_ERROR(buf != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlBuffer");
writer = xmlNewTextWriterMemory(buf, 0);
! XML_REPORT_ERROR(writer != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate xmlTextWriter");
if (xmlbinary == XMLBINARY_BASE64)
xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
*************** map_sql_value_to_xml_value(Datum value,
*** 1788,1799 ****
--- 1950,1966 ----
xmlFreeTextWriter(writer);
if (buf)
xmlBufferFree(buf);
+
+ pg_xml_done();
+
PG_RE_THROW();
}
PG_END_TRY();
xmlBufferFree(buf);
+ pg_xml_done();
+
return result;
}
#endif /* USE_LIBXML */
*************** xpath_internal(text *xpath_expr_text, xm
*** 3381,3387 ****
memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
xpath_expr[xpath_len] = '\0';
! pg_xml_init();
xmlInitParser();
PG_TRY();
--- 3548,3554 ----
memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
xpath_expr[xpath_len] = '\0';
! pg_xml_init(XML_PURPOSE_OTHER);
xmlInitParser();
PG_TRY();
*************** xpath_internal(text *xpath_expr_text, xm
*** 3391,3411 ****
* command execution are possible)
*/
ctxt = xmlNewParserCtxt();
! if (ctxt == NULL)
! xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate parser context");
doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0);
! if (doc == NULL)
! xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
! "could not parse XML document");
xpathctx = xmlXPathNewContext(doc);
! if (xpathctx == NULL)
! xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate XPath context");
xpathctx->node = xmlDocGetRootElement(doc);
! if (xpathctx->node == NULL)
! xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
! "could not find root XML element");
/* register namespaces, if any */
if (ns_count > 0)
--- 3558,3574 ----
* command execution are possible)
*/
ctxt = xmlNewParserCtxt();
! XML_REPORT_ERROR(ctxt != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate parser context");
doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0);
! XML_REPORT_ERROR(doc != NULL, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
! "could not parse XML document");
xpathctx = xmlXPathNewContext(doc);
! XML_REPORT_ERROR(xpathctx != NULL, ERROR, ERRCODE_OUT_OF_MEMORY,
! "could not allocate XPath context");
xpathctx->node = xmlDocGetRootElement(doc);
! XML_REPORT_ERROR(xpathctx->node != NULL, ERROR, ERRCODE_INTERNAL_ERROR,
! "could not find root XML element");
/* register namespaces, if any */
if (ns_count > 0)
*************** xpath_internal(text *xpath_expr_text, xm
*** 3432,3440 ****
}
xpathcomp = xmlXPathCompile(xpath_expr);
! if (xpathcomp == NULL) /* TODO: show proper XPath error details */
! xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
! "invalid XPath expression");
/*
* Version 2.6.27 introduces a function named
--- 3595,3602 ----
}
xpathcomp = xmlXPathCompile(xpath_expr);
! XML_REPORT_ERROR(xpathcomp != NULL, ERROR, ERRCODE_INTERNAL_ERROR,
! "invalid XPath expression");
/*
* Version 2.6.27 introduces a function named
*************** xpath_internal(text *xpath_expr_text, xm
*** 3444,3452 ****
* the same.
*/
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
! if (xpathobj == NULL) /* TODO: reason? */
! xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
! "could not create XPath object");
/* return empty array in cases when nothing is found */
if (xpathobj->nodesetval == NULL)
--- 3606,3613 ----
* the same.
*/
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
! XML_REPORT_ERROR(xpathobj != NULL, ERROR, ERRCODE_INTERNAL_ERROR,
! "could not create XPath object");
/* return empty array in cases when nothing is found */
if (xpathobj->nodesetval == NULL)
*************** xpath_internal(text *xpath_expr_text, xm
*** 3481,3486 ****
--- 3642,3650 ----
xmlFreeDoc(doc);
if (ctxt)
xmlFreeParserCtxt(ctxt);
+
+ pg_xml_done();
+
PG_RE_THROW();
}
PG_END_TRY();
*************** xpath_internal(text *xpath_expr_text, xm
*** 3490,3495 ****
--- 3654,3661 ----
xmlXPathFreeContext(xpathctx);
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
+
+ pg_xml_done();
}
#endif /* USE_LIBXML */
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6359cd6..6735521 100644
*** a/src/include/utils/xml.h
--- b/src/include/utils/xml.h
*************** typedef enum
*** 68,74 ****
XML_STANDALONE_OMITTED
} XmlStandaloneType;
! extern void pg_xml_init(void);
extern void xml_ereport(int level, int sqlcode, const char *msg);
extern xmltype *xmlconcat(List *args);
extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
--- 68,93 ----
XML_STANDALONE_OMITTED
} XmlStandaloneType;
! typedef enum
! {
! XML_PURPOSE_NONE /* Don't setup error handler. pg_xml_done() not required */,
! XML_PURPOSE_LEGACY /* Save error message only, don't set error flag */,
! XML_PURPOSE_WELLFORMED /* Ignore non-parser errors, nothing else*/,
! XML_PURPOSE_OTHER /* Report all errors */
! } XmlPurposeType;
!
! extern bool xml_error_occured;
!
! #define XML_REPORT_ERROR(assertion,level,sqlcode,msg) \
! if (pg_xml_erroroccurred() || !(assertion)) { \
! xml_ereport(level, sqlcode, msg); \
! } \
! else { \
! }
!
! extern void pg_xml_init(XmlPurposeType purpose);
! extern void pg_xml_done(void);
! extern bool pg_xml_erroroccurred(void);
extern void xml_ereport(int level, int sqlcode, const char *msg);
extern xmltype *xmlconcat(List *args);
extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index eaa5a74..da2f290 100644
*** a/src/test/regress/expected/xml.out
--- b/src/test/regress/expected/xml.out
*************** INSERT INTO xmltest VALUES (3, '', NULL, '');
--- 62,68 ----
ERROR: invalid XML content
LINE 1: SELECT xmlconcat('bad', '', NULL, '');
*************** SELECT xmlparse(content 'x');
*** 206,214 ****
x
(1 row)
SELECT xmlparse(document 'abc');
ERROR: invalid XML document
! DETAIL: Entity: line 1: parser error : Start tag expected, '<' not found
abc
^
SELECT xmlparse(document 'x');
--- 206,259 ----
x
(1 row)
+ SELECT xmlparse(content '&');
+ ERROR: invalid XML content
+ DETAIL: line 1: xmlParseEntityRef: no name
+ &
+ ^
+ line 1: chunk is not well balanced
+ &
+ ^
+ SELECT xmlparse(content '&idontexist;');
+ ERROR: invalid XML content
+ DETAIL: line 1: Entity 'idontexist' not defined
+ &idontexist;
+ ^
+ line 1: chunk is not well balanced
+ &idontexist;
+ ^
+ SELECT xmlparse(content '');
+ xmlparse
+ ---------------------------
+
+ (1 row)
+
+ SELECT xmlparse(content '');
+ xmlparse
+ --------------------------------
+
+ (1 row)
+
+ SELECT xmlparse(content '&idontexist;');
+ ERROR: invalid XML content
+ DETAIL: line 1: Entity 'idontexist' not defined
+ &idontexist;
+ ^
+ line 1: Opening and ending tag mismatch: twoerrors line 1 and unbalanced
+ &idontexist;
+ ^
+ line 1: chunk is not well balanced
+ &idontexist;
+ ^
+ SELECT xmlparse(content '');
+ xmlparse
+ ---------------------
+
+ (1 row)
+
SELECT xmlparse(document 'abc');
ERROR: invalid XML document
! DETAIL: line 1: Start tag expected, '<' not found
abc
^
SELECT xmlparse(document 'x');
*************** SELECT xmlparse(document 'x')
*** 217,222 ****
--- 262,309 ----
x
(1 row)
+ SELECT xmlparse(document '&');
+ ERROR: invalid XML document
+ DETAIL: line 1: xmlParseEntityRef: no name
+ &
+ ^
+ line 1: Opening and ending tag mismatch: invalidentity line 1 and abc
+ &
+ ^
+ SELECT xmlparse(document '&idontexist;');
+ ERROR: invalid XML document
+ DETAIL: line 1: Entity 'idontexist' not defined
+ &idontexist;
+ ^
+ line 1: Opening and ending tag mismatch: undefinedentity line 1 and abc
+ &idontexist;
+ ^
+ SELECT xmlparse(document '');
+ xmlparse
+ ---------------------------
+
+ (1 row)
+
+ SELECT xmlparse(document '');
+ xmlparse
+ --------------------------------
+
+ (1 row)
+
+ SELECT xmlparse(document '&idontexist;');
+ ERROR: invalid XML document
+ DETAIL: line 1: Entity 'idontexist' not defined
+ &idontexist;
+ ^
+ line 1: Opening and ending tag mismatch: twoerrors line 1 and unbalanced
+ &idontexist;
+ ^
+ SELECT xmlparse(document '');
+ xmlparse
+ ---------------------
+
+ (1 row)
+
SELECT xmlpi(name foo);
xmlpi
---------
*************** SELECT '<>' IS NOT DOCUMENT;
*** 379,385 ****
ERROR: invalid XML content
LINE 1: SELECT '<>' IS NOT DOCUMENT;
^
! DETAIL: Entity: line 1: parser error : StartTag: invalid element name
<>
^
SELECT xmlagg(data) FROM xmltest;
--- 466,472 ----
ERROR: invalid XML content
LINE 1: SELECT '<>' IS NOT DOCUMENT;
^
! DETAIL: line 1: StartTag: invalid element name
<>
^
SELECT xmlagg(data) FROM xmltest;
*************** EXECUTE foo ('bad');
*** 425,431 ****
ERROR: invalid XML document
LINE 1: EXECUTE foo ('bad');
^
! DETAIL: Entity: line 1: parser error : Start tag expected, '<' not found
bad
^
SET XML OPTION CONTENT;
--- 512,518 ----
ERROR: invalid XML document
LINE 1: EXECUTE foo ('bad');
^
! DETAIL: line 1: Start tag expected, '<' not found
bad
^
SET XML OPTION CONTENT;
*************** SELECT xml_is_well_formed('&');
+ xml_is_well_formed
+ --------------------
+ f
+ (1 row)
+
+ SELECT xml_is_well_formed('&idontexist;');
+ xml_is_well_formed
+ --------------------
+ f
+ (1 row)
+
+ SELECT xml_is_well_formed('');
+ xml_is_well_formed
+ --------------------
+ t
+ (1 row)
+
+ SELECT xml_is_well_formed('');
+ xml_is_well_formed
+ --------------------
+ t
+ (1 row)
+
+ SELECT xml_is_well_formed('&idontexist;');
+ xml_is_well_formed
+ --------------------
+ f
+ (1 row)
+
SET xmloption TO CONTENT;
SELECT xml_is_well_formed('abc');
xml_is_well_formed
*************** SELECT xml_is_well_formed('abc');
*** 686,688 ****
--- 803,838 ----
t
(1 row)
+ -- Since xpath() deals with namespaces, it's a bit stricter about
+ -- what's well-formed and what's not. If we don't obey these rules
+ -- (i.e. ignore namespace-related errors from libxml), xpath()
+ -- fails in subtle ways. The following would for example produce
+ -- the xml value
+ --
+ -- which is invalid beecause '<' may not appear un-escaped in
+ -- attribute values.
+ SELECT xpath('/*', '');
+ ERROR: could not parse XML document
+ DETAIL: xmlns: '<' is not a valid URI
+
+ ^
+ CONTEXT: SQL function "xpath" statement 1
+ -- Again, the XML isn't well-formed for namespace purposes
+ SELECT xpath('/*', '');
+ ERROR: could not parse XML document
+ DETAIL: Namespace prefix nosuchprefix on tag is not defined
+
+ ^
+ CONTEXT: SQL function "xpath" statement 1
+ -- XPath deprecates relative namespaces, but they're not supposed to
+ -- thrown an error, only a warning.
+ SELECT xpath('/*', '');
+ WARNING: xmlns: URI relative is not absolute
+
+ ^
+ CONTEXT: SQL function "xpath" statement 1
+ xpath
+ --------------------------------------
+ {""}
+ (1 row)
+
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 711b435..7cbc174 100644
*** a/src/test/regress/expected/xml_1.out
--- b/src/test/regress/expected/xml_1.out
*************** SELECT xmlparse(content 'x');
*** 172,177 ****
--- 172,201 ----
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(content '&');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(content '&idontexist;');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(content '');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(content '');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(content '&idontexist;');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(content '');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
SELECT xmlparse(document 'abc');
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
*************** SELECT xmlparse(document 'x')
*** 180,185 ****
--- 204,233 ----
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(document '&');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(document '&idontexist;');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(document '');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(document '');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(document '&idontexist;');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xmlparse(document '');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
SELECT xmlpi(name foo);
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
*************** SELECT xml_is_well_formed('&');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xml_is_well_formed('&idontexist;');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xml_is_well_formed('');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xml_is_well_formed('');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ SELECT xml_is_well_formed('&idontexist;');
+ ERROR: unsupported XML feature
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
SET xmloption TO CONTENT;
SELECT xml_is_well_formed('abc');
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml.
+ -- Since xpath() deals with namespaces, it's a bit stricter about
+ -- what's well-formed and what's not. If we don't obey these rules
+ -- (i.e. ignore namespace-related errors from libxml), xpath()
+ -- fails in subtle ways. The following would for example produce
+ -- the xml value
+ --
+ -- which is invalid beecause '<' may not appear un-escaped in
+ -- attribute values.
+ SELECT xpath('/*', '');
+ ERROR: unsupported XML feature
+ LINE 1: SELECT xpath('/*', '');
+ ^
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ -- Again, the XML isn't well-formed for namespace purposes
+ SELECT xpath('/*', '');
+ ERROR: unsupported XML feature
+ LINE 1: SELECT xpath('/*', '');
+ ^
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
+ -- XPath deprecates relative namespaces, but they're not supposed to
+ -- thrown an error, only a warning.
+ SELECT xpath('/*', '');
+ ERROR: unsupported XML feature
+ LINE 1: SELECT xpath('/*', '');
+ ^
+ DETAIL: This functionality requires the server to be built with libxml support.
+ HINT: You need to rebuild PostgreSQL using --with-libxml.
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 717a1e7..03830b9 100644
*** a/src/test/regress/sql/xml.sql
--- b/src/test/regress/sql/xml.sql
*************** SELECT xmlelement(name foo, xmlattribute
*** 62,70 ****
--- 62,82 ----
SELECT xmlparse(content 'abc');
SELECT xmlparse(content 'x');
+ SELECT xmlparse(content '&');
+ SELECT xmlparse(content '&idontexist;');
+ SELECT xmlparse(content '');
+ SELECT xmlparse(content '');
+ SELECT xmlparse(content '&idontexist;');
+ SELECT xmlparse(content '');
SELECT xmlparse(document 'abc');
SELECT xmlparse(document 'x');
+ SELECT xmlparse(document '&');
+ SELECT xmlparse(document '&idontexist;');
+ SELECT xmlparse(document '');
+ SELECT xmlparse(document '');
+ SELECT xmlparse(document '&idontexist;');
+ SELECT xmlparse(document '');
SELECT xmlpi(name foo);
*************** SELECT xml_is_well_formed('baz
*** 208,213 ****
--- 220,247 ----
SELECT xml_is_well_formed('number one');
SELECT xml_is_well_formed('bar');
SELECT xml_is_well_formed('bar');
+ SELECT xml_is_well_formed('&');
+ SELECT xml_is_well_formed('&idontexist;');
+ SELECT xml_is_well_formed('');
+ SELECT xml_is_well_formed('');
+ SELECT xml_is_well_formed('&idontexist;');
SET xmloption TO CONTENT;
SELECT xml_is_well_formed('abc');
+
+ -- Since xpath() deals with namespaces, it's a bit stricter about
+ -- what's well-formed and what's not. If we don't obey these rules
+ -- (i.e. ignore namespace-related errors from libxml), xpath()
+ -- fails in subtle ways. The following would for example produce
+ -- the xml value
+ --
+ -- which is invalid beecause '<' may not appear un-escaped in
+ -- attribute values.
+ SELECT xpath('/*', '');
+
+ -- Again, the XML isn't well-formed for namespace purposes
+ SELECT xpath('/*', '');
+
+ -- XPath deprecates relative namespaces, but they're not supposed to
+ -- thrown an error, only a warning.
+ SELECT xpath('/*', '');