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('/*', '');