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 <libxml/globals.h>],
+               [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..8170267 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(PgXmlStrictness 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(PgXmlStrictness strictness)
  {
  	/* Set up error handling (we share the core's error handler) */
! 	pg_xml_init(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..6008bc4 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(PgXmlStrictness 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..c5e3dac 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,106 ----
  
  #ifdef USE_LIBXML
  
+ #define XML_CHECK_AND_EREPORT(assertion,level,sqlcode,msg) \
+ 	if (pg_xml_erroroccurred() || !(assertion)) { \
+ 		xml_ereport(level, sqlcode, msg); \
+ 	} \
+ 	else { \
+ 	}
+ 
+ static xmlStructuredErrorFunc xml_structuredErrorFunc_saved = NULL;
+ static void* xml_structuredErrorContext_saved = NULL;
+ 
+ static PgXmlStrictness 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);
  
--- 611,626 ----
  	}
  
  	/* 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 ****
--- 656,672 ----
  			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;
  
--- 860,874 ----
  #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)
*** 883,891 ****
  		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 */
  		xml_memory_init();
--- 892,897 ----
*************** 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);
  	}
  }
  
  
--- 902,1003 ----
  
  		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(PgXmlStrictness strictness)
! {
! 	pg_xml_init_library();
! 
! 	Assert(strictness != PG_XML_STRICTNESS_NONE);
! 
! 	/* Imbalanced calls of pg_xml_init() and pg_xml_done() */
! 	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);
! }
! 
! 
! /*
!  * pg_xml_done --- restore libxml state after pg_xml_init().
!  *
!  * Resets libxml's global state (i.e. the structured error handler)
!  * to what it was originally before pg_xml_init() was called.
!  *
!  * This routine verifies that all pending errors have been dealt
!  * with (in assert-enabled builds, anyway).
!  *
!  * It is OK to call xml_ereport() after pg_xml_done(), though -
!  * 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_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;
! }
! 
! 
! 
! /*
!  * 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)
--- 1056,1066 ----
  	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)
--- 1214,1219 ----
*************** 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);
--- 1264,1271 ----
  	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)
  		{
  			/*
--- 1276,1291 ----
  										   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
  		{
--- 1300,1307 ----
  								 "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;
  }
  
--- 1324,1350 ----
  
  			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),
--- 1445,1450 ----
*************** 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);
  	}
  }
  
--- 1460,1618 ----
  
  
  /*
!  * 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),
--- 1972,1987 ----
  					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 ****
--- 2002,2018 ----
  							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();
--- 3600,3606 ----
  	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)
--- 3610,3626 ----
  		 * 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
--- 3647,3654 ----
  		}
  
  		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)
--- 3658,3665 ----
  		 * 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 ****
--- 3694,3702 ----
  			xmlFreeDoc(doc);
  		if (ctxt)
  			xmlFreeParserCtxt(ctxt);
+ 
+ 		pg_xml_done();
+ 
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
*************** xpath_internal(text *xpath_expr_text, xm
*** 3490,3495 ****
--- 3706,3713 ----
  	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..1baaabe 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 */,
! } PgXmlStrictness;
! 
! extern void pg_xml_init_library(void);
! extern void pg_xml_init(PgXmlStrictness 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, '<wrong')
*** 8,14 ****
  ERROR:  invalid XML content
  LINE 1: INSERT INTO xmltest VALUES (3, '<wrong');
                                         ^
! DETAIL:  Entity: line 1: parser error : Couldn't find end of Start Tag wrong line 1
  <wrong
        ^
  SELECT * FROM xmltest;
--- 8,14 ----
  ERROR:  invalid XML content
  LINE 1: INSERT INTO xmltest VALUES (3, '<wrong');
                                         ^
! DETAIL:  line 1: Couldn't find end of Start Tag wrong line 1
  <wrong
        ^
  SELECT * FROM xmltest;
*************** SELECT xmlconcat('bad', '<syntax');
*** 62,68 ****
  ERROR:  invalid XML content
  LINE 1: SELECT xmlconcat('bad', '<syntax');
                                  ^
! DETAIL:  Entity: line 1: parser error : Couldn't find end of Start Tag syntax line 1
  <syntax
         ^
  SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
--- 62,68 ----
  ERROR:  invalid XML content
  LINE 1: SELECT xmlconcat('bad', '<syntax');
                                  ^
! DETAIL:  line 1: Couldn't find end of Start Tag syntax line 1
  <syntax
         ^
  SELECT xmlconcat('<foo/>', NULL, '<?xml version="1.1" standalone="no"?><bar/>');
*************** SELECT xmlparse(content '<abc>x</abc>');
*** 206,214 ****
   <abc>x</abc>
  (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 '<abc>x</abc>');
--- 206,259 ----
   <abc>x</abc>
  (1 row)
  
+ SELECT xmlparse(content '<invalidentity>&</invalidentity>');
+ ERROR:  invalid XML content
+ DETAIL:  line 1: xmlParseEntityRef: no name
+ <invalidentity>&</invalidentity>
+                 ^
+ line 1: chunk is not well balanced
+ <invalidentity>&</invalidentity>
+                                 ^
+ SELECT xmlparse(content '<undefinedentity>&idontexist;</undefinedentity>');
+ ERROR:  invalid XML content
+ DETAIL:  line 1: Entity 'idontexist' not defined
+ <undefinedentity>&idontexist;</undefinedentity>
+                              ^
+ line 1: chunk is not well balanced
+ <undefinedentity>&idontexist;</undefinedentity>
+                                                ^
+ SELECT xmlparse(content '<invalidns xmlns=''&lt;''/>');
+          xmlparse          
+ ---------------------------
+  <invalidns xmlns='&lt;'/>
+ (1 row)
+ 
+ SELECT xmlparse(content '<relativens xmlns=''relative''/>');
+             xmlparse            
+ --------------------------------
+  <relativens xmlns='relative'/>
+ (1 row)
+ 
+ SELECT xmlparse(content '<twoerrors>&idontexist;</unbalanced>');
+ ERROR:  invalid XML content
+ DETAIL:  line 1: Entity 'idontexist' not defined
+ <twoerrors>&idontexist;</unbalanced>
+                        ^
+ line 1: Opening and ending tag mismatch: twoerrors line 1 and unbalanced
+ <twoerrors>&idontexist;</unbalanced>
+                                     ^
+ line 1: chunk is not well balanced
+ <twoerrors>&idontexist;</unbalanced>
+                                     ^
+ SELECT xmlparse(content '<nosuchprefix:tag/>');
+       xmlparse       
+ ---------------------
+  <nosuchprefix:tag/>
+ (1 row)
+ 
  SELECT xmlparse(document 'abc');
  ERROR:  invalid XML document
! DETAIL:  line 1: Start tag expected, '<' not found
  abc
  ^
  SELECT xmlparse(document '<abc>x</abc>');
*************** SELECT xmlparse(document '<abc>x</abc>')
*** 217,222 ****
--- 262,309 ----
   <abc>x</abc>
  (1 row)
  
+ SELECT xmlparse(document '<invalidentity>&</abc>');
+ ERROR:  invalid XML document
+ DETAIL:  line 1: xmlParseEntityRef: no name
+ <invalidentity>&</abc>
+                 ^
+ line 1: Opening and ending tag mismatch: invalidentity line 1 and abc
+ <invalidentity>&</abc>
+                       ^
+ SELECT xmlparse(document '<undefinedentity>&idontexist;</abc>');
+ ERROR:  invalid XML document
+ DETAIL:  line 1: Entity 'idontexist' not defined
+ <undefinedentity>&idontexist;</abc>
+                              ^
+ line 1: Opening and ending tag mismatch: undefinedentity line 1 and abc
+ <undefinedentity>&idontexist;</abc>
+                                    ^
+ SELECT xmlparse(document '<invalidns xmlns=''&lt;''/>');
+          xmlparse          
+ ---------------------------
+  <invalidns xmlns='&lt;'/>
+ (1 row)
+ 
+ SELECT xmlparse(document '<relativens xmlns=''relative''/>');
+             xmlparse            
+ --------------------------------
+  <relativens xmlns='relative'/>
+ (1 row)
+ 
+ SELECT xmlparse(document '<twoerrors>&idontexist;</unbalanced>');
+ ERROR:  invalid XML document
+ DETAIL:  line 1: Entity 'idontexist' not defined
+ <twoerrors>&idontexist;</unbalanced>
+                        ^
+ line 1: Opening and ending tag mismatch: twoerrors line 1 and unbalanced
+ <twoerrors>&idontexist;</unbalanced>
+                                     ^
+ SELECT xmlparse(document '<nosuchprefix:tag/>');
+       xmlparse       
+ ---------------------
+  <nosuchprefix:tag/>
+ (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('<pg:foo xmlns
*** 679,684 ****
--- 766,801 ----
   t
  (1 row)
  
+ SELECT xml_is_well_formed('<invalidentity>&</abc>');
+  xml_is_well_formed 
+ --------------------
+  f
+ (1 row)
+ 
+ SELECT xml_is_well_formed('<undefinedentity>&idontexist;</abc>');
+  xml_is_well_formed 
+ --------------------
+  f
+ (1 row)
+ 
+ SELECT xml_is_well_formed('<invalidns xmlns=''&lt;''/>');
+  xml_is_well_formed 
+ --------------------
+  t
+ (1 row)
+ 
+ SELECT xml_is_well_formed('<relativens xmlns=''relative''/>');
+  xml_is_well_formed 
+ --------------------
+  t
+ (1 row)
+ 
+ SELECT xml_is_well_formed('<twoerrors>&idontexist;</unbalanced>');
+  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
+ --   <invalidns xmlns='<'/>
+ -- 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('/*', '<invalidns xmlns=''&lt;''/>');
+ ERROR:  could not parse XML document
+ \set VERBOSITY default
+ -- Again, the XML isn't well-formed for namespace purposes
+ SELECT xpath('/*', '<nosuchprefix:tag/>');
+ ERROR:  could not parse XML document
+ DETAIL:  line 1: Namespace prefix nosuchprefix on tag is not defined
+ <nosuchprefix:tag/>
+                  ^
+ CONTEXT:  SQL function "xpath" statement 1
+ -- XPath deprecates relative namespaces, but they're not supposed to
+ -- thrown an error, only a warning.
+ SELECT xpath('/*', '<relativens xmlns=''relative''/>');
+ WARNING:  line 1: xmlns: URI relative is not absolute
+ <relativens xmlns='relative'/>
+                             ^
+ CONTEXT:  SQL function "xpath" statement 1
+                 xpath                 
+ --------------------------------------
+  {"<relativens xmlns=\"relative\"/>"}
+ (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 '<abc>x</abc>');
*** 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 '<invalidentity>&</invalidentity>');
+ 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 '<undefinedentity>&idontexist;</undefinedentity>');
+ 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 '<invalidns xmlns=''&lt;''/>');
+ 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 '<relativens xmlns=''relative''/>');
+ 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 '<twoerrors>&idontexist;</unbalanced>');
+ 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 '<nosuchprefix:tag/>');
+ 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 '<abc>x</abc>')
*** 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 '<invalidentity>&</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.
+ SELECT xmlparse(document '<undefinedentity>&idontexist;</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.
+ SELECT xmlparse(document '<invalidns xmlns=''&lt;''/>');
+ 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 '<relativens xmlns=''relative''/>');
+ 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 '<twoerrors>&idontexist;</unbalanced>');
+ 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 '<nosuchprefix:tag/>');
+ 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('<pg:foo xmlns
*** 627,634 ****
--- 675,731 ----
  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('<invalidentity>&</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.
+ SELECT xml_is_well_formed('<undefinedentity>&idontexist;</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.
+ SELECT xml_is_well_formed('<invalidns xmlns=''&lt;''/>');
+ 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('<relativens xmlns=''relative''/>');
+ 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('<twoerrors>&idontexist;</unbalanced>');
+ 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
+ --   <invalidns xmlns='<'/>
+ -- 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('/*', '<invalidns xmlns=''&lt;''/>');
+ ERROR:  unsupported XML feature at character 20
+ \set VERBOSITY default
+ -- Again, the XML isn't well-formed for namespace purposes
+ SELECT xpath('/*', '<nosuchprefix:tag/>');
+ ERROR:  unsupported XML feature
+ LINE 1: SELECT xpath('/*', '<nosuchprefix:tag/>');
+                            ^
+ 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('/*', '<relativens xmlns=''relative''/>');
+ ERROR:  unsupported XML feature
+ LINE 1: SELECT xpath('/*', '<relativens xmlns=''relative''/>');
+                            ^
+ 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 '<abc>x</abc>');
+ SELECT xmlparse(content '<invalidentity>&</invalidentity>');
+ SELECT xmlparse(content '<undefinedentity>&idontexist;</undefinedentity>');
+ SELECT xmlparse(content '<invalidns xmlns=''&lt;''/>');
+ SELECT xmlparse(content '<relativens xmlns=''relative''/>');
+ SELECT xmlparse(content '<twoerrors>&idontexist;</unbalanced>');
+ SELECT xmlparse(content '<nosuchprefix:tag/>');
  
  SELECT xmlparse(document 'abc');
  SELECT xmlparse(document '<abc>x</abc>');
+ SELECT xmlparse(document '<invalidentity>&</abc>');
+ SELECT xmlparse(document '<undefinedentity>&idontexist;</abc>');
+ SELECT xmlparse(document '<invalidns xmlns=''&lt;''/>');
+ SELECT xmlparse(document '<relativens xmlns=''relative''/>');
+ SELECT xmlparse(document '<twoerrors>&idontexist;</unbalanced>');
+ SELECT xmlparse(document '<nosuchprefix:tag/>');
  
  
  SELECT xmlpi(name foo);
*************** SELECT xml_is_well_formed('<foo><bar>baz
*** 208,213 ****
--- 220,251 ----
  SELECT xml_is_well_formed('<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>');
  SELECT xml_is_well_formed('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</my:foo>');
  SELECT xml_is_well_formed('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</pg:foo>');
+ SELECT xml_is_well_formed('<invalidentity>&</abc>');
+ SELECT xml_is_well_formed('<undefinedentity>&idontexist;</abc>');
+ SELECT xml_is_well_formed('<invalidns xmlns=''&lt;''/>');
+ SELECT xml_is_well_formed('<relativens xmlns=''relative''/>');
+ SELECT xml_is_well_formed('<twoerrors>&idontexist;</unbalanced>');
  
  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
+ --   <invalidns xmlns='<'/>
+ -- 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('/*', '<invalidns xmlns=''&lt;''/>');
+ \set VERBOSITY default
+ 
+ -- Again, the XML isn't well-formed for namespace purposes
+ SELECT xpath('/*', '<nosuchprefix:tag/>');
+ 
+ -- XPath deprecates relative namespaces, but they're not supposed to
+ -- thrown an error, only a warning.
+ SELECT xpath('/*', '<relativens xmlns=''relative''/>');
