diff options
Diffstat (limited to 'src/backend/utils/adt/xml.c')
-rw-r--r-- | src/backend/utils/adt/xml.c | 541 |
1 files changed, 402 insertions, 139 deletions
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 945bc2901fa..6786cd91bb5 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -85,11 +85,27 @@ int xmloption; #ifdef USE_LIBXML -static StringInfo xml_err_buf = NULL; - -static void xml_errorHandler(void *ctxt, const char *msg,...); +/* random number to identify PgXmlErrorContext */ +#define ERRCXT_MAGIC 68275028 + +struct PgXmlErrorContext +{ + int magic; + /* strictness argument passed to pg_xml_init */ + PgXmlStrictness strictness; + /* current error status and accumulated message, if any */ + bool err_occurred; + StringInfoData err_buf; + /* previous libxml error handling state (saved by pg_xml_init) */ + xmlStructuredErrorFunc saved_errfunc; + void *saved_errcxt; +}; + +static void xml_errorHandler(void *data, xmlErrorPtr error); static void xml_ereport_by_code(int level, int sqlcode, const char *msg, int errcode); +static void chopStringInfoNewlines(StringInfo str); +static void appendStringInfoLineSeparator(StringInfo str); #ifdef USE_LIBXMLCONTEXT @@ -552,8 +568,9 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) int i; ListCell *arg; ListCell *narg; - xmlBufferPtr buf = NULL; - xmlTextWriterPtr writer = NULL; + PgXmlErrorContext *xmlerrcxt; + volatile xmlBufferPtr buf = NULL; + volatile xmlTextWriterPtr writer = NULL; /* * We first evaluate all the arguments, then start up libxml and create @@ -598,17 +615,17 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) } /* now safe to run libxml */ - pg_xml_init(); + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); PG_TRY(); { buf = xmlBufferCreate(); - if (!buf) - xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + if (buf == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate xmlBuffer"); writer = xmlNewTextWriterMemory(buf, 0); - if (!writer) - xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + if (writer == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate xmlTextWriter"); xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name); @@ -645,12 +662,17 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) xmlFreeTextWriter(writer); if (buf) xmlBufferFree(buf); + + pg_xml_done(xmlerrcxt, true); + PG_RE_THROW(); } PG_END_TRY(); xmlBufferFree(buf); + pg_xml_done(xmlerrcxt, false); + return result; #else NO_XML_SUPPORT(); @@ -800,7 +822,7 @@ xml_is_document(xmltype *arg) { #ifdef USE_LIBXML bool result; - xmlDocPtr doc = NULL; + volatile xmlDocPtr doc = NULL; MemoryContext ccxt = CurrentMemoryContext; /* We want to catch ereport(INVALID_XML_DOCUMENT) and return false */ @@ -844,30 +866,24 @@ xml_is_document(xmltype *arg) #ifdef USE_LIBXML /* - * pg_xml_init --- set up for use of libxml + * pg_xml_init_library --- set up for use of libxml * * 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. + * facilities but doesn't require error handling. It initializes libxml + * and verifies compatibility with the loaded libxml version. These are + * once-per-session activities. * * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and * check) */ void -pg_xml_init(void) +pg_xml_init_library(void) { static bool first_time = true; if (first_time) { /* Stuff we need do only once per session */ - MemoryContext oldcontext; /* * Currently, we have no pure UTF-8 support for internals -- check if @@ -879,16 +895,8 @@ pg_xml_init(void) errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.", (int) sizeof(char), (int) sizeof(xmlChar)))); - /* 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 */ + /* Set up libxml's memory allocation our way */ xml_memory_init(); #endif @@ -897,21 +905,119 @@ pg_xml_init(void) 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); - } +/* + * pg_xml_init --- set up for use of libxml and register an error handler + * + * This should be called by each function that is about to use libxml + * facilities and requires error handling. It initializes libxml with + * pg_xml_init_library() and establishes our libxml error handler. + * + * strictness determines which errors are reported and which are ignored. + * + * Calls to this function MUST be followed by a PG_TRY block that guarantees + * that pg_xml_done() is called during either normal or error exit. + * + * 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. + */ +PgXmlErrorContext * +pg_xml_init(PgXmlStrictness strictness) +{ + PgXmlErrorContext *errcxt; + + /* Do one-time setup if needed */ + pg_xml_init_library(); + + /* Create error handling context structure */ + errcxt = (PgXmlErrorContext *) palloc(sizeof(PgXmlErrorContext)); + errcxt->magic = ERRCXT_MAGIC; + errcxt->strictness = strictness; + errcxt->err_occurred = false; + initStringInfo(&errcxt->err_buf); + + /* + * Save original error handler and install ours. libxml originally didn't + * distinguish between the contexts for generic and for structured error + * handlers. If we're using an old libxml version, we must thus save + * the generic error context, even though we're using a structured + * error handler. + */ + errcxt->saved_errfunc = xmlStructuredError; + +#ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT + errcxt->saved_errcxt = xmlStructuredErrorContext; +#else + errcxt->saved_errcxt = xmlGenericErrorContext; +#endif + + xmlSetStructuredErrorFunc((void *) errcxt, xml_errorHandler); + + return errcxt; +} + + +/* + * pg_xml_done --- restore previous libxml error handling + * + * Resets libxml's global error-handling state to what it was before + * pg_xml_init() was called. + * + * This routine verifies that all pending errors have been dealt with + * (in assert-enabled builds, anyway). + */ +void +pg_xml_done(PgXmlErrorContext *errcxt, bool isError) +{ + void *cur_errcxt; + + /* An assert seems like enough protection here */ + Assert(errcxt->magic == ERRCXT_MAGIC); + + /* + * In a normal exit, there should be no un-handled libxml errors. But we + * shouldn't try to enforce this during error recovery, since the longjmp + * could have been thrown before xml_ereport had a chance to run. + */ + Assert(!errcxt->err_occurred || isError); + + /* + * Check that libxml's global state is correct, warn if not. This is + * a real test and not an Assert because it has a higher probability + * of happening. + */ +#ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT + cur_errcxt = xmlStructuredErrorContext; +#else + cur_errcxt = xmlGenericErrorContext; +#endif + + if (cur_errcxt != (void *) errcxt) + elog(WARNING, "libxml error handling state is out of sync with xml.c"); + + /* Restore the saved handler */ + xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc); + + /* + * Mark the struct as invalid, just in case somebody somehow manages to + * call xml_errorHandler or xml_ereport with it. + */ + errcxt->magic = 0; + + /* Release memory */ + pfree(errcxt->err_buf.data); + pfree(errcxt); +} + + +/* + * pg_xml_error_occurred() --- test the error flag + */ +bool +pg_xml_error_occurred(PgXmlErrorContext *errcxt) +{ + return errcxt->err_occurred; } @@ -970,7 +1076,13 @@ parse_xml_decl(const xmlChar *str, size_t *lenp, int utf8char; int utf8len; - pg_xml_init(); + /* + * 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. Note that this is safe (and a no-op) if caller has already + * done pg_xml_init(). + */ + pg_xml_init_library(); /* Initialize output arguments to "not present" */ if (version) @@ -1124,8 +1236,6 @@ static bool 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) @@ -1176,8 +1286,9 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, int32 len; xmlChar *string; xmlChar *utf8string; - xmlParserCtxtPtr ctxt; - xmlDocPtr doc; + PgXmlErrorContext *xmlerrcxt; + volatile xmlParserCtxtPtr ctxt = NULL; + volatile xmlDocPtr doc = NULL; len = VARSIZE(data) - VARHDRSZ; /* will be useful later */ string = xml_text2xmlChar(data); @@ -1187,18 +1298,19 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, encoding, PG_UTF8); - /* Start up libxml and its parser (no-ops if already done) */ - pg_xml_init(); - xmlInitParser(); + /* Start up libxml and its parser */ + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED); - 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 */ + /* Use a TRY block to ensure we clean up correctly */ PG_TRY(); { + xmlInitParser(); + + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate parser context"); + if (xmloption_arg == XMLOPTION_DOCUMENT) { /* @@ -1213,8 +1325,8 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, "UTF-8", XML_PARSE_NOENT | XML_PARSE_DTDATTR | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS)); - if (doc == NULL) - xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, + if (doc == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT, "invalid XML document"); } else @@ -1238,23 +1350,28 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL); - if (res_code != 0) - { - xmlFreeDoc(doc); - xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT, + if (res_code != 0 || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT, "invalid XML content"); - } } } PG_CATCH(); { - xmlFreeParserCtxt(ctxt); + if (doc != NULL) + xmlFreeDoc(doc); + if (ctxt != NULL) + xmlFreeParserCtxt(ctxt); + + pg_xml_done(xmlerrcxt, true); + PG_RE_THROW(); } PG_END_TRY(); xmlFreeParserCtxt(ctxt); + pg_xml_done(xmlerrcxt, false); + return doc; } @@ -1335,70 +1452,180 @@ xml_pstrdup(const char *string) * handler. Note that pg_xml_init() *must* have been called previously. */ void -xml_ereport(int level, int sqlcode, const char *msg) +xml_ereport(PgXmlErrorContext *errcxt, 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); - } + /* Defend against someone passing us a bogus context struct */ + if (errcxt->magic != ERRCXT_MAGIC) + elog(ERROR, "xml_ereport called with invalid PgXmlErrorContext"); + + /* Flag that the current libxml error has been reported */ + errcxt->err_occurred = false; + + /* Include detail only if we have some text from libxml */ + if (errcxt->err_buf.len > 0) + detail = errcxt->err_buf.data; else detail = NULL; - if (detail) + ereport(level, + (errcode(sqlcode), + errmsg_internal("%s", msg), + detail ? errdetail_internal("%s", detail) : 0)); +} + + +/* + * Error handler for libxml errors and warnings + */ +static void +xml_errorHandler(void *data, xmlErrorPtr error) +{ + PgXmlErrorContext *xmlerrcxt = (PgXmlErrorContext *) data; + 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; + int domain = error->domain; + int level = error->level; + StringInfo errorBuf; + + /* Defend against someone passing us a bogus context struct */ + if (xmlerrcxt->magic != ERRCXT_MAGIC) + elog(ERROR, "xml_errorHandler called with invalid PgXmlErrorContext"); + + /*---------- + * Older libxml versions report some errors differently. + * First, some errors were previously reported as coming from the parser + * domain but are now reported as coming from the namespace domain. + * Second, some warnings were upgraded to errors. + * We attempt to compensate for that here. + *---------- + */ + switch (error->code) { - size_t len; + case XML_WAR_NS_URI: + level = XML_ERR_ERROR; + domain = XML_FROM_NAMESPACE; + break; + + case XML_ERR_NS_DECL_ERROR: + case XML_WAR_NS_URI_RELATIVE: + case XML_WAR_NS_COLUMN: + case XML_NS_ERR_XML_NAMESPACE: + case XML_NS_ERR_UNDEFINED_NAMESPACE: + case XML_NS_ERR_QNAME: + case XML_NS_ERR_ATTRIBUTE_REDEFINED: + case XML_NS_ERR_EMPTY: + domain = XML_FROM_NAMESPACE; + break; + } - /* libxml error messages end in '\n'; get rid of it */ - len = strlen(detail); - if (len > 0 && detail[len - 1] == '\n') - detail[len - 1] = '\0'; + /* Decide whether to act on the error or not */ + switch (domain) + { + case XML_FROM_PARSER: + case XML_FROM_NONE: + case XML_FROM_MEMORY: + case XML_FROM_IO: + /* Accept error regardless of the parsing purpose */ + break; - ereport(level, - (errcode(sqlcode), - errmsg_internal("%s", msg), - errdetail_internal("%s", detail))); + default: + /* Ignore error if only doing well-formedness check */ + if (xmlerrcxt->strictness == PG_XML_STRICTNESS_WELLFORMED) + return; + break; } - else + + /* Prepare error message in errorBuf */ + errorBuf = makeStringInfo(); + + if (error->line > 0) + appendStringInfo(errorBuf, "line %d: ", error->line); + if (name != NULL) + appendStringInfo(errorBuf, "element %s: ", name); + appendStringInfoString(errorBuf, error->message); + + /* + * Append context information to errorBuf. + * + * xmlParserPrintFileContext() uses libxml's "generic" error handler to + * write the context. Since we don't want to duplicate libxml + * functionality here, we set up a generic error handler temporarily. + * + * We use appendStringInfo() directly as libxml's generic error handler. + * This should work because it has essentially the same signature as + * libxml expects, namely (void *ptr, const char *msg, ...). + */ + if (input != NULL) { - ereport(level, - (errcode(sqlcode), - errmsg_internal("%s", msg))); + xmlGenericErrorFunc errFuncSaved = xmlGenericError; + void *errCtxSaved = xmlGenericErrorContext; + + xmlSetGenericErrorFunc((void *) errorBuf, + (xmlGenericErrorFunc) appendStringInfo); + + /* Add context information to errorBuf */ + appendStringInfoLineSeparator(errorBuf); + + xmlParserPrintFileContext(input); + + /* Restore generic error func */ + xmlSetGenericErrorFunc(errCtxSaved, errFuncSaved); } -} + /* Get rid of any trailing newlines in errorBuf */ + chopStringInfoNewlines(errorBuf); -/* - * Error handler for libxml error messages - */ -static void -xml_errorHandler(void *ctxt, const char *msg,...) -{ - /* Append the formatted text to xml_err_buf */ - for (;;) + /* + * Legacy error handling mode. err_occurred is never set, we just add the + * message to err_buf. This mode 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. This is also why we don't + * distinguish between notices, warnings and errors here --- the old-style + * generic error handler wouldn't have done that either. + */ + if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY) { - va_list args; - bool success; + appendStringInfoLineSeparator(&xmlerrcxt->err_buf); + appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data); - /* Try to format the data. */ - va_start(args, msg); - success = appendStringInfoVA(xml_err_buf, msg, args); - va_end(args); + pfree(errorBuf->data); + pfree(errorBuf); + return; + } - if (success) - break; + /* + * 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 can be reported immediately since they won't cause + * a longjmp() out of libxml. + */ + if (level >= XML_ERR_ERROR) + { + appendStringInfoLineSeparator(&xmlerrcxt->err_buf); + appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data); - /* Double the buffer size and try again. */ - enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen); + xmlerrcxt->err_occurred = true; + } + else if (level >= XML_ERR_WARNING) + { + ereport(WARNING, + (errmsg_internal("%s", errorBuf->data))); + } + else + { + ereport(NOTICE, + (errmsg_internal("%s", errorBuf->data))); } + + pfree(errorBuf->data); + pfree(errorBuf); } @@ -1448,6 +1675,29 @@ xml_ereport_by_code(int level, int sqlcode, /* + * Remove all trailing newlines from a StringInfo string + */ +static void +chopStringInfoNewlines(StringInfo str) +{ + while (str->len > 0 && str->data[str->len - 1] == '\n') + str->data[--str->len] = '\0'; +} + + +/* + * Append a newline after removing any existing trailing newlines + */ +static void +appendStringInfoLineSeparator(StringInfo str) +{ + chopStringInfoNewlines(str); + if (str->len > 0) + appendStringInfoChar(str, '\n'); +} + + +/* * Convert one char in the current server encoding to a Unicode codepoint. */ static pg_wchar @@ -1753,21 +2003,22 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings) case BYTEAOID: { bytea *bstr = DatumGetByteaPP(value); - xmlBufferPtr buf = NULL; - xmlTextWriterPtr writer = NULL; + PgXmlErrorContext *xmlerrcxt; + volatile xmlBufferPtr buf = NULL; + volatile xmlTextWriterPtr writer = NULL; char *result; - pg_xml_init(); + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); PG_TRY(); { buf = xmlBufferCreate(); - if (!buf) - xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + if (buf == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate xmlBuffer"); writer = xmlNewTextWriterMemory(buf, 0); - if (!writer) - xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + if (writer == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate xmlTextWriter"); if (xmlbinary == XMLBINARY_BASE64) @@ -1789,12 +2040,17 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings) xmlFreeTextWriter(writer); if (buf) xmlBufferFree(buf); + + pg_xml_done(xmlerrcxt, true); + PG_RE_THROW(); } PG_END_TRY(); xmlBufferFree(buf); + pg_xml_done(xmlerrcxt, false); + return result; } #endif /* USE_LIBXML */ @@ -3312,11 +3568,12 @@ static void xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, int *res_nitems, ArrayBuildState **astate) { - xmlParserCtxtPtr ctxt = NULL; - xmlDocPtr doc = NULL; - xmlXPathContextPtr xpathctx = NULL; - xmlXPathCompExprPtr xpathcomp = NULL; - xmlXPathObjectPtr xpathobj = NULL; + PgXmlErrorContext *xmlerrcxt; + volatile xmlParserCtxtPtr ctxt = NULL; + volatile xmlDocPtr doc = NULL; + volatile xmlXPathContextPtr xpathctx = NULL; + volatile xmlXPathCompExprPtr xpathcomp = NULL; + volatile xmlXPathObjectPtr xpathobj = NULL; char *datastr; int32 len; int32 xpath_len; @@ -3382,30 +3639,31 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len); xpath_expr[xpath_len] = '\0'; - pg_xml_init(); - xmlInitParser(); + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); PG_TRY(); { + xmlInitParser(); + /* * redundant XML parsing (two parsings for the same value during one * command execution are possible) */ ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) - xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + if (ctxt == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, 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, + if (doc == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT, "could not parse XML document"); xpathctx = xmlXPathNewContext(doc); - if (xpathctx == NULL) - xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + if (xpathctx == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate XPath context"); xpathctx->node = xmlDocGetRootElement(doc); - if (xpathctx->node == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, + if (xpathctx->node == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, "could not find root XML element"); /* register namespaces, if any */ @@ -3433,8 +3691,8 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, } xpathcomp = xmlXPathCompile(xpath_expr); - if (xpathcomp == NULL) /* TODO: show proper XPath error details */ - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, + if (xpathcomp == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, "invalid XPath expression"); /* @@ -3445,8 +3703,8 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, * the same. */ xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx); - if (xpathobj == NULL) /* TODO: reason? */ - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, + if (xpathobj == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, "could not create XPath object"); /* return empty array in cases when nothing is found */ @@ -3482,6 +3740,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, xmlFreeDoc(doc); if (ctxt) xmlFreeParserCtxt(ctxt); + + pg_xml_done(xmlerrcxt, true); + PG_RE_THROW(); } PG_END_TRY(); @@ -3491,6 +3752,8 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, xmlXPathFreeContext(xpathctx); xmlFreeDoc(doc); xmlFreeParserCtxt(ctxt); + + pg_xml_done(xmlerrcxt, false); } #endif /* USE_LIBXML */ @@ -3579,7 +3842,7 @@ static bool wellformed_xml(text *data, XmlOptionType xmloption_arg) { bool result; - xmlDocPtr doc = NULL; + volatile xmlDocPtr doc = NULL; /* We want to catch any exceptions and return false */ PG_TRY(); |