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