diff --git a/configure b/configure index 9b8cb48..c4c535c 100755 *** a/configure --- b/configure *************** with_gnu_ld *** 850,855 **** --- 850,856 ---- enable_largefile enable_float4_byval enable_float8_byval + enable_float8_byval ' ac_precious_vars='build_alias host_alias *************** LDFLAGS *** 860,865 **** --- 861,867 ---- LIBS CPPFLAGS CPP + CPPFLAGS LDFLAGS_EX LDFLAGS_SL DOCBOOKSTYLE' *************** done *** 5740,5745 **** --- 5742,5812 ---- esac done fi + + # Cannot use AC_CHECK_FUNC because xmlStructuredErrorContext may be a + # macro if threading support is enabled in libxml2 + { $as_echo "$as_me:$LINENO: checking for xmlStructuredErrorContext" >&5 + $as_echo_n "checking for xmlStructuredErrorContext... " >&6; } + if test "${pgac_cv_libxml_structerrctx+set}" = set; then + $as_echo_n "(cached) " >&6 + else + cat >conftest.$ac_ext <<_ACEOF + /* confdefs.h. */ + _ACEOF + cat confdefs.h >>conftest.$ac_ext + cat >>conftest.$ac_ext <<_ACEOF + /* end confdefs.h. */ + #include + int + main () + { + void* d = xmlStructuredErrorContext + ; + return 0; + } + _ACEOF + rm -f conftest.$ac_objext conftest$ac_exeext + if { (ac_try="$ac_link" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac + eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" + $as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + pgac_cv_libxml_structerrctx=yes + else + $as_echo "$as_me: failed program was:" >&5 + sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv_libxml_structerrctx=no + fi + + rm -rf conftest.dSYM + rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + fi + { $as_echo "$as_me:$LINENO: result: $pgac_cv_libxml_structerrctx" >&5 + $as_echo "$pgac_cv_libxml_structerrctx" >&6; } + if test x"$pgac_cv_libxml_structerrctx" = x"yes"; then + + cat >>confdefs.h <<\_ACEOF + #define HAVE_LIBXML2_XMLSTRUCTUREDERRORCONTEXT 1 + _ACEOF + + fi fi diff --git a/configure.in b/configure.in index e873c7b..6f9f0c4 100644 *** a/configure.in --- b/configure.in *************** if test "$with_libxml" = yes ; then *** 741,746 **** --- 741,759 ---- esac done fi + + # Cannot use AC_CHECK_FUNC because xmlStructuredErrorContext may be a + # macro if threading support is enabled in libxml2 + AC_CACHE_CHECK([for xmlStructuredErrorContext], pgac_cv_libxml_structerrctx, + [AC_TRY_LINK([#include ], + [void* d = xmlStructuredErrorContext], + [pgac_cv_libxml_structerrctx=yes], + [pgac_cv_libxml_structerrctx=no])]) + if test x"$pgac_cv_libxml_structerrctx" = x"yes"; then + AC_DEFINE(HAVE_LIBXML2_XMLSTRUCTUREDERRORCONTEXT, + 1, + [Define to 1 if your libxml has xmlStructuredErrorContext.]) + fi fi AC_SUBST(with_libxml) diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 44c600e..f3417ab 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(PgXmlStrictnessType strictness); /* 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(PgXmlStrictnessType strictness) { /* Set up error handling (we share the core's error handler) */ ! pg_xml_init_errorhandling(strictness); /* 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(PG_XML_STRICTNESS_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,416 **** workspace->ctxt = NULL; workspace->res = NULL; ! pgxml_parser_init(); workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize); ! if (workspace->doctree == NULL) return NULL; /* not well-formed */ workspace->ctxt = xmlXPathNewContext(workspace->doctree); workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree); --- 409,421 ---- workspace->ctxt = NULL; workspace->res = NULL; ! pgxml_parser_init(PG_XML_STRICTNESS_LEGACY); workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize); ! if (workspace->doctree == NULL) { ! pg_xml_done(); return NULL; /* not well-formed */ + } workspace->ctxt = xmlXPathNewContext(workspace->doctree); workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree); *************** pgxml_xpath(text *document, xmlChar *xpa *** 420,425 **** --- 425,431 ---- 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 **** --- 439,446 ---- 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++) --- 667,673 ---- * 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(PG_XML_STRICTNESS_LEGACY); /* For each row i.e. document returned from SPI */ for (i = 0; i < proc; i++) *************** xpath_table(PG_FUNCTION_ARGS) *** 720,725 **** --- 728,734 ---- 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 **** --- 793,800 ---- 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..0ebca21 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(PgXmlStrictnessType strictness); /* 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(PG_XML_STRICTNESS_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..a09c13e 100644 *** a/src/backend/utils/adt/xml.c --- b/src/backend/utils/adt/xml.c *************** int xmloption; *** 84,92 **** #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); --- 84,107 ---- #ifdef USE_LIBXML + #define XML_CHECK_AND_EREPORT(assertion,level,sqlcode,msg) \ + if (pg_xml_erroroccurred() || !(assertion)) { \ + xml_ereport(level, sqlcode, msg); \ + } \ + else { \ + } + + static bool xml_error_initialized = false; + static xmlStructuredErrorFunc xml_structuredErrorFunc_saved = NULL; + static void* xml_structuredErrorContext_saved = NULL; + + static PgXmlStrictnessType xml_strictness = PG_XML_STRICTNESS_NONE; + static bool xml_err_occurred; static StringInfo xml_err_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); --- 612,627 ---- } /* now safe to run libxml */ ! pg_xml_init(PG_XML_STRICTNESS_ALL); PG_TRY(); { buf = xmlBufferCreate(); ! XML_CHECK_AND_EREPORT(buf != NULL, ERROR, ERRCODE_OUT_OF_MEMORY, ! "could not allocate xmlBuffer"); writer = xmlNewTextWriterMemory(buf, 0); ! XML_CHECK_AND_EREPORT(writer != NULL, ERROR, ERRCODE_OUT_OF_MEMORY, ! "could not allocate xmlTextWriter"); xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name); *************** xmlelement(XmlExprState *xmlExpr, ExprCo *** 644,655 **** --- 657,673 ---- 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) *** 843,865 **** #ifdef USE_LIBXML /* ! * pg_xml_init --- 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. ! * ! * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and ! * check) */ void ! pg_xml_init(void) { static bool first_time = true; --- 861,875 ---- #ifdef USE_LIBXML /* ! * pg_xml_init_library --- setup for use of libxml * * This should be called by each function that is about to use libxml ! * facilities but doesn't require error handling. It initializes libxml ! * and verifies compatibility with the loaded libxml version (done on first ! * call in a session). */ void ! pg_xml_init_library(void) { static bool first_time = true; *************** pg_xml_init(void) *** 882,890 **** 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 */ --- 892,898 ---- oldcontext = MemoryContextSwitchTo(TopMemoryContext); xml_err_buf = makeStringInfo(); MemoryContextSwitchTo(oldcontext); ! xml_err_occurred = 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); } } --- 904,1008 ---- first_time = false; } ! } ! /* ! * 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 initialized libxml with ! * pg_xml_init_library() and establishes our libxml error handler. ! * The strictness determines which errors are reported and which are ignored ! * ! * 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. ! * ! * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and ! * check) ! */ ! void ! pg_xml_init(PgXmlStrictnessType strictness) ! { ! pg_xml_init_library(); ! ! Assert(strictness != PG_XML_STRICTNESS_NONE); ! ! /* Imbalanced calls of pg_xml_init() and pg_xml_done() */ ! Assert(!xml_error_initialized); ! Assert(xml_strictness == PG_XML_STRICTNESS_NONE); ! ! /* Set strictness level */ ! xml_strictness = strictness; ! ! /* Reset pre-existing buffer to empty */ ! Assert(xml_err_buf != NULL); ! resetStringInfo(xml_err_buf); ! xml_err_occurred = false; ! ! /* Save original error handler and install ours. libxml originally didn't ! * distinguish between the context for generic and for structured error ! * handler. If we're using such an libxml version, we must thus save ! * the generic error context, even though we're using a structured ! * error handler. ! */ ! xml_structuredErrorFunc_saved = xmlStructuredError; ! #if HAVE_LIBXML2_XMLSTRUCTUREDERRORCONTEXT ! xml_structuredErrorContext_saved = xmlStructuredErrorContext; ! #else ! xml_structuredErrorContext_saved = xmlGenericErrorContext; ! #endif ! 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 PG_XML_STRICTNESS_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_err_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_strictness != PG_XML_STRICTNESS_NONE); ! ! /* Errors should have been dealt with */ ! Assert(!xml_err_occurred); ! ! xmlSetStructuredErrorFunc(xml_structuredErrorContext_saved, ! xml_structuredErrorFunc_saved); ! xml_strictness = PG_XML_STRICTNESS_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_err_occurred) { ! xml_err_occurred = 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) --- 1061,1071 ---- 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_library(); /* 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) --- 1219,1224 ---- *************** 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); --- 1269,1276 ---- 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) { /* --- 1281,1296 ---- PG_UTF8); /* Start up libxml and its parser (no-ops if already done) */ ! pg_xml_init(PG_XML_STRICTNESS_WELLFORMED); xmlInitParser(); /* Use a TRY block to ensure the ctxt is released */ PG_TRY(); { + ctxt = xmlNewParserCtxt(); + XML_CHECK_AND_EREPORT(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 { --- 1305,1312 ---- "UTF-8", XML_PARSE_NOENT | XML_PARSE_DTDATTR | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS)); ! XML_CHECK_AND_EREPORT(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; } --- 1329,1355 ---- res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL); ! XML_CHECK_AND_EREPORT(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; } *************** xml_ereport(int level, int sqlcode, cons *** 1354,1366 **** 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), --- 1450,1455 ---- *************** 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); } } --- 1465,1623 ---- /* ! * 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) ! { ! int domain; ! int level; ! 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; ! ! /* 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 ! */ ! domain = error->domain; ! level = error->level; ! switch (error->code) { ! 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; ! } ! ! /* 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 regardless of the parsing purpose */ ! break; ! ! default: ! /* Ignore during well-formedness check */ ! if (xml_strictness == PG_XML_STRICTNESS_WELLFORMED) ! return; ! break; ! } ! ! /* Append error message to xml_err_buf */ ! if (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_err_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_err_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. ! * 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 (xml_strictness == PG_XML_STRICTNESS_LEGACY) ! { ! appendStringInfoLineSeparator(xml_err_buf); ! appendStringInfoString(xml_err_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 (level >= XML_ERR_ERROR) ! { ! appendStringInfoLineSeparator(xml_err_buf); ! appendStringInfoString(xml_err_buf, errorBuf->data); ! xml_err_occurred = true; ! } ! else if (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), --- 1977,1992 ---- xmlTextWriterPtr writer = NULL; char *result; ! pg_xml_init(PG_XML_STRICTNESS_ALL); PG_TRY(); { buf = xmlBufferCreate(); ! XML_CHECK_AND_EREPORT(buf != NULL, ERROR, ERRCODE_OUT_OF_MEMORY, ! "could not allocate xmlBuffer"); writer = xmlNewTextWriterMemory(buf, 0); ! XML_CHECK_AND_EREPORT(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 **** --- 2007,2023 ---- 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(); --- 3605,3611 ---- memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len); xpath_expr[xpath_len] = '\0'; ! pg_xml_init(PG_XML_STRICTNESS_ALL); 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) --- 3615,3631 ---- * command execution are possible) */ ctxt = xmlNewParserCtxt(); ! XML_CHECK_AND_EREPORT(ctxt != NULL, ERROR, ERRCODE_OUT_OF_MEMORY, ! "could not allocate parser context"); doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0); ! XML_CHECK_AND_EREPORT(doc != NULL, ERROR, ERRCODE_INVALID_XML_DOCUMENT, ! "could not parse XML document"); xpathctx = xmlXPathNewContext(doc); ! XML_CHECK_AND_EREPORT(xpathctx != NULL, ERROR, ERRCODE_OUT_OF_MEMORY, ! "could not allocate XPath context"); xpathctx->node = xmlDocGetRootElement(doc); ! XML_CHECK_AND_EREPORT(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 --- 3652,3659 ---- } xpathcomp = xmlXPathCompile(xpath_expr); ! XML_CHECK_AND_EREPORT(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) --- 3663,3670 ---- * the same. */ xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx); ! XML_CHECK_AND_EREPORT(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 **** --- 3699,3707 ---- xmlFreeDoc(doc); if (ctxt) xmlFreeParserCtxt(ctxt); + + pg_xml_done(); + PG_RE_THROW(); } PG_END_TRY(); *************** xpath_internal(text *xpath_expr_text, xm *** 3490,3495 **** --- 3711,3718 ---- 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..bc4a74d 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,87 ---- XML_STANDALONE_OMITTED } XmlStandaloneType; ! typedef enum ! { ! PG_XML_STRICTNESS_NONE /* No error handling */, ! PG_XML_STRICTNESS_LEGACY /* Ignore notices/warnings/errors unless ! * function result indicates error condition ! */, ! PG_XML_STRICTNESS_WELLFORMED/* Ignore non-parser notices/warnings/errors */, ! PG_XML_STRICTNESS_ALL /* Report all notices/warnings/errors */, ! } PgXmlStrictnessType; ! ! extern void pg_xml_init_library(void); ! extern void pg_xml_init(PgXmlStrictnessType strictness); ! 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..364ce51 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. + -- Since different libxml versions emit slightly different + -- error messages, we suppress them + \set VERBOSITY terse + SELECT xpath('/*', ''); + ERROR: could not parse XML document + \set VERBOSITY default + -- Again, the XML isn't well-formed for namespace purposes + SELECT xpath('/*', ''); + ERROR: could not parse XML document + DETAIL: line 1: 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: line 1: 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..75e78a0 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. + -- Since different libxml versions emit slightly different + -- error messages, we suppress them + \set VERBOSITY terse + SELECT xpath('/*', ''); + ERROR: unsupported XML feature at character 20 + \set VERBOSITY default + -- 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..b1a3a2c 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,251 ---- 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. + -- Since different libxml versions emit slightly different + -- error messages, we suppress them + \set VERBOSITY terse + SELECT xpath('/*', ''); + \set VERBOSITY default + + -- 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('/*', '');