remove contrib/xml2

Started by Robert Haasalmost 16 years ago26 messages
#1Robert Haas
robertmhaas@gmail.com
1 attachment(s)

There has been some more discussion lately of problems caused by contrib/xml2.

http://archives.postgresql.org/pgsql-bugs/2010-01/msg00251.php
http://archives.postgresql.org/pgsql-bugs/2010-01/msg00198.php

I think we need to either (1) fix the bugs and update the
documentation to remove the statement that this will be removed or (2)
actually remove it. Nobody seems interested in #1, so PFA a patch to
do #2. It also rips out all the libxslt stuff, which seems to exist
only for the purpose of supporting contrib/xml2. Per discussion on IM
with Magnus, it appears that libxslt is actually not needed to build
the backend on Windows, despite a comment in the Windows build stuff
to the contrary.

Thoughts?

...Robert

Attachments:

remove-contrib-xml2.patchtext/x-patch; charset=US-ASCII; name=remove-contrib-xml2.patchDownload
diff --git a/configure.in b/configure.in
index 5a27d3a..352d7b2 100644
--- a/configure.in
+++ b/configure.in
@@ -732,15 +732,6 @@ fi
 AC_SUBST(with_libxml)
 
 #
-# XSLT
-#
-PGAC_ARG_BOOL(with, libxslt, no, [use XSLT support when building contrib/xml2],
-              [AC_DEFINE([USE_LIBXSLT], 1, [Define to 1 to use XSLT support when building contrib/xml2. (--with-libxslt)])])
-
-
-AC_SUBST(with_libxslt)
-
-#
 # tzdata
 #
 PGAC_ARG_REQ(with, system-tzdata,
@@ -943,10 +934,6 @@ if test "$with_libxml" = yes ; then
   AC_CHECK_LIB(xml2, xmlSaveToBuffer, [], [AC_MSG_ERROR([library 'xml2' (version >= 2.6.23) is required for XML support])])
 fi
 
-if test "$with_libxslt" = yes ; then
-  AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
-fi
-
 # for contrib/uuid-ossp
 if test "$with_ossp_uuid" = yes ; then
   AC_CHECK_LIB(ossp-uuid, uuid_export,
@@ -1051,10 +1038,6 @@ if test "$with_libxml" = yes ; then
   AC_CHECK_HEADER(libxml/parser.h, [], [AC_MSG_ERROR([header file <libxml/parser.h> is required for XML support])])
 fi
 
-if test "$with_libxslt" = yes ; then
-  AC_CHECK_HEADER(libxslt/xslt.h, [], [AC_MSG_ERROR([header file <libxslt/xslt.h> is required for XSLT support])])
-fi
-
 if test "$with_ldap" = yes ; then
   if test "$PORTNAME" != "win32"; then
      AC_CHECK_HEADERS(ldap.h, [],
diff --git a/contrib/Makefile b/contrib/Makefile
index 19d5c1b..168396c 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -51,10 +51,6 @@ ifeq ($(with_ossp_uuid),yes)
 SUBDIRS += uuid-ossp
 endif
 
-ifeq ($(with_libxml),yes)
-SUBDIRS += xml2
-endif
-
 # Missing:
 #		start-scripts	\ (does not have a makefile)
 
diff --git a/contrib/xml2/Makefile b/contrib/xml2/Makefile
deleted file mode 100644
index ea8636a..0000000
--- a/contrib/xml2/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-# $PostgreSQL$
-
-MODULE_big = pgxml
-
-OBJS = $(if $(filter -lxslt, $(LIBS)), xpath.o xslt_proc.o, xpath.o)
-
-SHLIB_LINK += $(filter -lxslt, $(LIBS)) $(filter -lxml2, $(LIBS))
-
-DATA_built = pgxml.sql
-DATA = uninstall_pgxml.sql
-
-ifdef USE_PGXS
-PG_CONFIG = pg_config
-PGXS := $(shell $(PG_CONFIG) --pgxs)
-include $(PGXS)
-else
-subdir = contrib/xml2
-top_builddir = ../..
-include $(top_builddir)/src/Makefile.global
-include $(top_srcdir)/contrib/contrib-global.mk
-endif
diff --git a/contrib/xml2/pgxml.sql.in b/contrib/xml2/pgxml.sql.in
deleted file mode 100644
index 139ca14..0000000
--- a/contrib/xml2/pgxml.sql.in
+++ /dev/null
@@ -1,86 +0,0 @@
-/* $PostgreSQL$ */
-
--- Adjust this setting to control where the objects get created.
-SET search_path = public;
-
---SQL for XML parser
-
-CREATE OR REPLACE FUNCTION xml_is_well_formed(text) RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
--- deprecated old name for xml_is_well_formed
-CREATE OR REPLACE FUNCTION xml_valid(text) RETURNS bool
-AS 'MODULE_PATHNAME', 'xml_is_well_formed'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION xml_encode_special_chars(text) RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION xpath_string(text,text) RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION xpath_nodeset(text,text,text,text) RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION xpath_number(text,text) RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION xpath_bool(text,text) RETURNS boolean
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
--- List function
-
-CREATE OR REPLACE FUNCTION xpath_list(text,text,text) RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-
-CREATE OR REPLACE FUNCTION xpath_list(text,text) RETURNS text
-AS 'SELECT xpath_list($1,$2,'','')'
-LANGUAGE SQL STRICT IMMUTABLE;
-
-
-
--- Wrapper functions for nodeset where no tags needed
-
-
-CREATE OR REPLACE FUNCTION xpath_nodeset(text,text)
-RETURNS text
-AS 'SELECT xpath_nodeset($1,$2,'''','''')'
-LANGUAGE SQL STRICT IMMUTABLE;
-
-
-CREATE OR REPLACE FUNCTION xpath_nodeset(text,text,text)
-RETURNS text
-AS 'SELECT xpath_nodeset($1,$2,'''',$3)'
-LANGUAGE SQL STRICT IMMUTABLE;
-
--- Table function
-
-CREATE OR REPLACE FUNCTION xpath_table(text,text,text,text,text)
-RETURNS setof record
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE;
-
--- XSLT functions
--- Delete from here to the end of the file if you are not compiling with
--- XSLT support.
-
-
-CREATE OR REPLACE FUNCTION xslt_process(text,text,text)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT VOLATILE;
-
--- the function checks for the correct argument count
-
-CREATE OR REPLACE FUNCTION xslt_process(text,text)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
diff --git a/contrib/xml2/uninstall_pgxml.sql b/contrib/xml2/uninstall_pgxml.sql
deleted file mode 100644
index ec676dc..0000000
--- a/contrib/xml2/uninstall_pgxml.sql
+++ /dev/null
@@ -1,33 +0,0 @@
-/* $PostgreSQL$ */
-
--- Adjust this setting to control where the objects get dropped.
-SET search_path = public;
-
-DROP FUNCTION xslt_process(text,text);
-
-DROP FUNCTION xslt_process(text,text,text);
-
-DROP FUNCTION xpath_table(text,text,text,text,text);
-
-DROP FUNCTION xpath_nodeset(text,text,text);
-
-DROP FUNCTION xpath_nodeset(text,text);
-
-DROP FUNCTION xpath_list(text,text);
-
-DROP FUNCTION xpath_list(text,text,text);
-
-DROP FUNCTION xpath_bool(text,text);
-
-DROP FUNCTION xpath_number(text,text);
-
-DROP FUNCTION xpath_nodeset(text,text,text,text);
-
-DROP FUNCTION xpath_string(text,text);
-
-DROP FUNCTION xml_encode_special_chars(text);
-
--- deprecated old name for xml_is_well_formed
-DROP FUNCTION xml_valid(text);
-
-DROP FUNCTION xml_is_well_formed(text);
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
deleted file mode 100644
index dd895b0..0000000
--- a/contrib/xml2/xpath.c
+++ /dev/null
@@ -1,933 +0,0 @@
-/*
- * $PostgreSQL$
- *
- * Parser interface for DOM-based parser (libxml) rather than
- * stream-based SAX-type parser
- */
-#include "postgres.h"
-
-#include "executor/spi.h"
-#include "fmgr.h"
-#include "funcapi.h"
-#include "lib/stringinfo.h"
-#include "miscadmin.h"
-#include "utils/builtins.h"
-
-/* libxml includes */
-
-#include <libxml/xpath.h>
-#include <libxml/tree.h>
-#include <libxml/xmlmemory.h>
-#include <libxml/xmlerror.h>
-#include <libxml/parserInternals.h>
-
-
-PG_MODULE_MAGIC;
-
-/* declarations */
-
-static void *pgxml_palloc(size_t size);
-static void *pgxml_repalloc(void *ptr, size_t size);
-static void pgxml_pfree(void *ptr);
-static char *pgxml_pstrdup(const char *string);
-static void pgxml_errorHandler(void *ctxt, const char *msg,...);
-
-void		elog_error(int level, char *explain, int force);
-void		pgxml_parser_init(void);
-
-static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
-				   xmlChar *toptagname, xmlChar *septagname,
-				   xmlChar *plainsep);
-
-text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
-					 xmlChar *septag, xmlChar *plainsep);
-
-xmlChar    *pgxml_texttoxmlchar(text *textstring);
-
-static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath);
-
-
-Datum		xml_is_well_formed(PG_FUNCTION_ARGS);
-Datum		xml_encode_special_chars(PG_FUNCTION_ARGS);
-Datum		xpath_nodeset(PG_FUNCTION_ARGS);
-Datum		xpath_string(PG_FUNCTION_ARGS);
-Datum		xpath_number(PG_FUNCTION_ARGS);
-Datum		xpath_bool(PG_FUNCTION_ARGS);
-Datum		xpath_list(PG_FUNCTION_ARGS);
-Datum		xpath_table(PG_FUNCTION_ARGS);
-
-/* Global variables */
-char	   *errbuf;				/* per line error buffer */
-char	   *pgxml_errorMsg = NULL;		/* overall error message */
-
-#define ERRBUF_SIZE 200
-
-/* memory handling passthrough functions (e.g. palloc, pstrdup are
-   currently macros, and the others might become so...) */
-
-static void *
-pgxml_palloc(size_t size)
-{
-/*	elog(DEBUG1,"Alloc %d in CMC %p",size,CurrentMemoryContext); */
-	return palloc(size);
-}
-
-static void *
-pgxml_repalloc(void *ptr, size_t size)
-{
-/*	elog(DEBUG1,"ReAlloc in CMC %p",CurrentMemoryContext);*/
-	return repalloc(ptr, size);
-}
-
-static void
-pgxml_pfree(void *ptr)
-{
-/*	elog(DEBUG1,"Free in CMC %p",CurrentMemoryContext); */
-	pfree(ptr);
-}
-
-static char *
-pgxml_pstrdup(const char *string)
-{
-	return pstrdup(string);
-}
-
-/* The error handling function. This formats an error message and sets
- * a flag - an ereport will be issued prior to return
- */
-
-static void
-pgxml_errorHandler(void *ctxt, const char *msg,...)
-{
-	va_list		args;
-
-	va_start(args, msg);
-	vsnprintf(errbuf, ERRBUF_SIZE, msg, args);
-	va_end(args);
-	/* Now copy the argument across */
-	if (pgxml_errorMsg == NULL)
-		pgxml_errorMsg = pstrdup(errbuf);
-	else
-	{
-		int32		xsize = strlen(pgxml_errorMsg);
-
-		pgxml_errorMsg = repalloc(pgxml_errorMsg,
-								  (size_t) (xsize + strlen(errbuf) + 1));
-		strncpy(&pgxml_errorMsg[xsize - 1], errbuf, strlen(errbuf));
-		pgxml_errorMsg[xsize + strlen(errbuf) - 1] = '\0';
-
-	}
-	memset(errbuf, 0, ERRBUF_SIZE);
-}
-
-/* This function reports the current message at the level specified */
-void
-elog_error(int level, char *explain, int force)
-{
-	if (force || (pgxml_errorMsg != NULL))
-	{
-		if (pgxml_errorMsg == NULL)
-		{
-			ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
-							errmsg("%s", explain)));
-		}
-		else
-		{
-			ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
-							errmsg("%s:%s", explain, pgxml_errorMsg)));
-			pfree(pgxml_errorMsg);
-		}
-	}
-}
-
-void
-pgxml_parser_init()
-{
-	/*
-	 * This code could also set parser settings from  user-supplied info.
-	 * Quite how these settings are made is another matter :)
-	 */
-
-	xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup);
-	xmlInitParser();
-
-	xmlSetGenericErrorFunc(NULL, pgxml_errorHandler);
-
-	xmlSubstituteEntitiesDefault(1);
-	xmlLoadExtDtdDefaultValue = 1;
-
-	pgxml_errorMsg = NULL;
-
-	errbuf = palloc(200);
-	memset(errbuf, 0, 200);
-
-}
-
-
-/* Returns true if document is well-formed */
-
-PG_FUNCTION_INFO_V1(xml_is_well_formed);
-
-Datum
-xml_is_well_formed(PG_FUNCTION_ARGS)
-{
-	/* called as xml_is_well_formed(document) */
-	xmlDocPtr	doctree;
-	text	   *t = PG_GETARG_TEXT_P(0);		/* document buffer */
-	int32		docsize = VARSIZE(t) - VARHDRSZ;
-
-	pgxml_parser_init();
-
-	doctree = xmlParseMemory((char *) VARDATA(t), docsize);
-	if (doctree == NULL)
-	{
-		xmlCleanupParser();
-		PG_RETURN_BOOL(false);	/* i.e. not well-formed */
-	}
-	xmlCleanupParser();
-	xmlFreeDoc(doctree);
-	PG_RETURN_BOOL(true);
-}
-
-
-/* Encodes special characters (<, >, &, " and \r) as XML entities */
-
-PG_FUNCTION_INFO_V1(xml_encode_special_chars);
-
-Datum
-xml_encode_special_chars(PG_FUNCTION_ARGS)
-{
-	text	   *tin = PG_GETARG_TEXT_P(0);
-	text	   *tout;
-	xmlChar    *ts,
-			   *tt;
-
-	ts = pgxml_texttoxmlchar(tin);
-
-	tt = xmlEncodeSpecialChars(NULL, ts);
-
-	pfree(ts);
-
-	tout = cstring_to_text((char *) tt);
-
-	xmlFree(tt);
-
-	PG_RETURN_TEXT_P(tout);
-}
-
-static xmlChar
-		   *
-pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
-				   xmlChar *toptagname,
-				   xmlChar *septagname,
-				   xmlChar *plainsep)
-{
-	/* Function translates a nodeset into a text representation */
-
-	/*
-	 * iterates over each node in the set and calls xmlNodeDump to write it to
-	 * an xmlBuffer -from which an xmlChar * string is returned.
-	 */
-
-	/* each representation is surrounded by <tagname> ... </tagname> */
-
-	/*
-	 * plainsep is an ordinary (not tag) seperator - if used, then nodes are
-	 * cast to string as output method
-	 */
-
-
-	xmlBufferPtr buf;
-	xmlChar    *result;
-	int			i;
-
-	buf = xmlBufferCreate();
-
-	if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
-	{
-		xmlBufferWriteChar(buf, "<");
-		xmlBufferWriteCHAR(buf, toptagname);
-		xmlBufferWriteChar(buf, ">");
-	}
-	if (nodeset != NULL)
-	{
-		for (i = 0; i < nodeset->nodeNr; i++)
-		{
-
-			if (plainsep != NULL)
-			{
-				xmlBufferWriteCHAR(buf,
-							  xmlXPathCastNodeToString(nodeset->nodeTab[i]));
-
-				/* If this isn't the last entry, write the plain sep. */
-				if (i < (nodeset->nodeNr) - 1)
-					xmlBufferWriteChar(buf, (char *) plainsep);
-			}
-			else
-			{
-
-
-				if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
-				{
-					xmlBufferWriteChar(buf, "<");
-					xmlBufferWriteCHAR(buf, septagname);
-					xmlBufferWriteChar(buf, ">");
-				}
-				xmlNodeDump(buf,
-							nodeset->nodeTab[i]->doc,
-							nodeset->nodeTab[i],
-							1, 0);
-
-				if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
-				{
-					xmlBufferWriteChar(buf, "</");
-					xmlBufferWriteCHAR(buf, septagname);
-					xmlBufferWriteChar(buf, ">");
-				}
-			}
-		}
-	}
-
-	if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
-	{
-		xmlBufferWriteChar(buf, "</");
-		xmlBufferWriteCHAR(buf, toptagname);
-		xmlBufferWriteChar(buf, ">");
-	}
-	result = xmlStrdup(buf->content);
-	xmlBufferFree(buf);
-	return result;
-}
-
-
-/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
- * into the libxml2 representation
- */
-
-xmlChar *
-pgxml_texttoxmlchar(text *textstring)
-{
-	return (xmlChar *) text_to_cstring(textstring);
-}
-
-/* Public visible XPath functions */
-
-/* This is a "raw" xpath function. Check that it returns child elements
- * properly
- */
-
-PG_FUNCTION_INFO_V1(xpath_nodeset);
-
-Datum
-xpath_nodeset(PG_FUNCTION_ARGS)
-{
-	xmlChar    *xpath,
-			   *toptag,
-			   *septag;
-	int32		pathsize;
-	text
-			   *xpathsupp,
-			   *xpres;
-
-	/* PG_GETARG_TEXT_P(0) is document buffer */
-	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
-
-	toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
-	septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
-
-	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
-
-	xpath = pgxml_texttoxmlchar(xpathsupp);
-
-	xpres = pgxml_result_to_text(
-								 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
-								 toptag, septag, NULL);
-
-	/* xmlCleanupParser(); done by result_to_text routine */
-	pfree(xpath);
-
-	if (xpres == NULL)
-		PG_RETURN_NULL();
-	PG_RETURN_TEXT_P(xpres);
-}
-
-/*	The following function is almost identical, but returns the elements in */
-/*	a list. */
-
-PG_FUNCTION_INFO_V1(xpath_list);
-
-Datum
-xpath_list(PG_FUNCTION_ARGS)
-{
-	xmlChar    *xpath,
-			   *plainsep;
-	int32		pathsize;
-	text
-			   *xpathsupp,
-			   *xpres;
-
-	/* PG_GETARG_TEXT_P(0) is document buffer */
-	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
-
-	plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
-
-	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
-
-	xpath = pgxml_texttoxmlchar(xpathsupp);
-
-	xpres = pgxml_result_to_text(
-								 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
-								 NULL, NULL, plainsep);
-
-	/* xmlCleanupParser(); done by result_to_text routine */
-	pfree(xpath);
-
-	if (xpres == NULL)
-		PG_RETURN_NULL();
-	PG_RETURN_TEXT_P(xpres);
-}
-
-
-PG_FUNCTION_INFO_V1(xpath_string);
-
-Datum
-xpath_string(PG_FUNCTION_ARGS)
-{
-	xmlChar    *xpath;
-	int32		pathsize;
-	text
-			   *xpathsupp,
-			   *xpres;
-
-	/* PG_GETARG_TEXT_P(0) is document buffer */
-	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
-
-	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
-
-	/*
-	 * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL
-	 * at end
-	 */
-	/* We could try casting to string using the libxml function? */
-
-	xpath = (xmlChar *) palloc(pathsize + 9);
-	memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
-	strncpy((char *) xpath, "string(", 7);
-	xpath[pathsize + 7] = ')';
-	xpath[pathsize + 8] = '\0';
-
-	xpres = pgxml_result_to_text(
-								 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
-								 NULL, NULL, NULL);
-
-	xmlCleanupParser();
-	pfree(xpath);
-
-	if (xpres == NULL)
-		PG_RETURN_NULL();
-	PG_RETURN_TEXT_P(xpres);
-}
-
-
-PG_FUNCTION_INFO_V1(xpath_number);
-
-Datum
-xpath_number(PG_FUNCTION_ARGS)
-{
-	xmlChar    *xpath;
-	int32		pathsize;
-	text
-			   *xpathsupp;
-
-	float4		fRes;
-
-	xmlXPathObjectPtr res;
-
-	/* PG_GETARG_TEXT_P(0) is document buffer */
-	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
-
-	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
-
-	xpath = pgxml_texttoxmlchar(xpathsupp);
-
-	res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
-	pfree(xpath);
-
-	if (res == NULL)
-	{
-		xmlCleanupParser();
-		PG_RETURN_NULL();
-	}
-
-	fRes = xmlXPathCastToNumber(res);
-	xmlCleanupParser();
-	if (xmlXPathIsNaN(fRes))
-		PG_RETURN_NULL();
-
-	PG_RETURN_FLOAT4(fRes);
-
-}
-
-
-PG_FUNCTION_INFO_V1(xpath_bool);
-
-Datum
-xpath_bool(PG_FUNCTION_ARGS)
-{
-	xmlChar    *xpath;
-	int32		pathsize;
-	text
-			   *xpathsupp;
-
-	int			bRes;
-
-	xmlXPathObjectPtr res;
-
-	/* PG_GETARG_TEXT_P(0) is document buffer */
-	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
-
-	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
-
-	xpath = pgxml_texttoxmlchar(xpathsupp);
-
-	res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
-	pfree(xpath);
-
-	if (res == NULL)
-	{
-		xmlCleanupParser();
-		PG_RETURN_BOOL(false);
-	}
-
-	bRes = xmlXPathCastToBoolean(res);
-	xmlCleanupParser();
-	PG_RETURN_BOOL(bRes);
-
-}
-
-
-
-/* Core function to evaluate XPath query */
-
-xmlXPathObjectPtr
-pgxml_xpath(text *document, xmlChar *xpath)
-{
-
-	xmlDocPtr	doctree;
-	xmlXPathContextPtr ctxt;
-	xmlXPathObjectPtr res;
-
-	xmlXPathCompExprPtr comppath;
-
-	int32		docsize;
-
-
-	docsize = VARSIZE(document) - VARHDRSZ;
-
-	pgxml_parser_init();
-
-	doctree = xmlParseMemory((char *) VARDATA(document), docsize);
-	if (doctree == NULL)
-	{							/* not well-formed */
-		return NULL;
-	}
-
-	ctxt = xmlXPathNewContext(doctree);
-	ctxt->node = xmlDocGetRootElement(doctree);
-
-
-	/* compile the path */
-	comppath = xmlXPathCompile(xpath);
-	if (comppath == NULL)
-	{
-		xmlCleanupParser();
-		xmlFreeDoc(doctree);
-		elog_error(ERROR, "XPath Syntax Error", 1);
-
-		return NULL;
-	}
-
-	/* Now evaluate the path expression. */
-	res = xmlXPathCompiledEval(comppath, ctxt);
-	xmlXPathFreeCompExpr(comppath);
-
-	if (res == NULL)
-	{
-		xmlXPathFreeContext(ctxt);
-		/* xmlCleanupParser(); */
-		xmlFreeDoc(doctree);
-
-		return NULL;
-	}
-	/* xmlFreeDoc(doctree); */
-	return res;
-}
-
-text
-		   *
-pgxml_result_to_text(xmlXPathObjectPtr res,
-					 xmlChar *toptag,
-					 xmlChar *septag,
-					 xmlChar *plainsep)
-{
-	xmlChar    *xpresstr;
-	text	   *xpres;
-
-	if (res == NULL)
-	{
-		xmlCleanupParser();
-		return NULL;
-	}
-	switch (res->type)
-	{
-		case XPATH_NODESET:
-			xpresstr = pgxmlNodeSetToText(res->nodesetval,
-										  toptag,
-										  septag, plainsep);
-			break;
-
-		case XPATH_STRING:
-			xpresstr = xmlStrdup(res->stringval);
-			break;
-
-		default:
-			elog(NOTICE, "unsupported XQuery result: %d", res->type);
-			xpresstr = xmlStrdup((const xmlChar *) "<unsupported/>");
-	}
-
-
-	/* Now convert this result back to text */
-	xpres = cstring_to_text((char *) xpresstr);
-
-	/* Free various storage */
-	xmlCleanupParser();
-	/* xmlFreeDoc(doctree);  -- will die at end of tuple anyway */
-
-	xmlFree(xpresstr);
-
-	elog_error(ERROR, "XPath error", 0);
-
-
-	return xpres;
-}
-
-/* xpath_table is a table function. It needs some tidying (as do the
- * other functions here!
- */
-
-PG_FUNCTION_INFO_V1(xpath_table);
-
-Datum
-xpath_table(PG_FUNCTION_ARGS)
-{
-/* SPI (input tuple) support */
-	SPITupleTable *tuptable;
-	HeapTuple	spi_tuple;
-	TupleDesc	spi_tupdesc;
-
-/* Output tuple (tuplestore) support */
-	Tuplestorestate *tupstore = NULL;
-	TupleDesc	ret_tupdesc;
-	HeapTuple	ret_tuple;
-
-	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	AttInMetadata *attinmeta;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
-
-/* Function parameters */
-	char	   *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0));
-	char	   *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1));
-	char	   *relname = text_to_cstring(PG_GETARG_TEXT_PP(2));
-	char	   *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3));
-	char	   *condition = text_to_cstring(PG_GETARG_TEXT_PP(4));
-
-	char	  **values;
-	xmlChar   **xpaths;
-	char	   *pos;
-	const char *pathsep = "|";
-
-	int			numpaths;
-	int			ret;
-	int			proc;
-	int			i;
-	int			j;
-	int			rownr;			/* For issuing multiple rows from one original
-								 * document */
-	int			had_values;		/* To determine end of nodeset results */
-
-	StringInfoData query_buf;
-
-	/* We only have a valid tuple description in table function mode */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (rsinfo->expectedDesc == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("xpath_table must be called as a table function")));
-
-	/*
-	 * We want to materialise because it means that we don't have to carry
-	 * libxml2 parser state between invocations of this function
-	 */
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-			   errmsg("xpath_table requires Materialize mode, but it is not "
-					  "allowed in this context")));
-
-	/*
-	 * The tuplestore must exist in a higher context than this function call
-	 * (per_query_ctx is used)
-	 */
-
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
-	/*
-	 * Create the tuplestore - work_mem is the max in-memory size before a
-	 * file is created on disk to hold it.
-	 */
-	tupstore =
-		tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
-							  false, work_mem);
-
-	MemoryContextSwitchTo(oldcontext);
-
-	/* get the requested return tuple description */
-	ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
-
-	/*
-	 * At the moment we assume that the returned attributes make sense for the
-	 * XPath specififed (i.e. we trust the caller). It's not fatal if they get
-	 * it wrong - the input function for the column type will raise an error
-	 * if the path result can't be converted into the correct binary
-	 * representation.
-	 */
-
-	attinmeta = TupleDescGetAttInMetadata(ret_tupdesc);
-
-	/* Set return mode and allocate value space. */
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setDesc = ret_tupdesc;
-
-	values = (char **) palloc(ret_tupdesc->natts * sizeof(char *));
-
-	xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));
-
-	/* Split XPaths. xpathset is a writable CString. */
-
-	/* Note that we stop splitting once we've done all needed for tupdesc */
-
-	numpaths = 0;
-	pos = xpathset;
-	do
-	{
-		xpaths[numpaths] = (xmlChar *) pos;
-		pos = strstr(pos, pathsep);
-		if (pos != NULL)
-		{
-			*pos = '\0';
-			pos++;
-		}
-		numpaths++;
-	} while ((pos != NULL) && (numpaths < (ret_tupdesc->natts - 1)));
-
-	/* Now build query */
-	initStringInfo(&query_buf);
-
-	/* Build initial sql statement */
-	appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s",
-					 pkeyfield,
-					 xmlfield,
-					 relname,
-					 condition
-		);
-
-
-	if ((ret = SPI_connect()) < 0)
-		elog(ERROR, "xpath_table: SPI_connect returned %d", ret);
-
-	if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
-		elog(ERROR, "xpath_table: SPI execution failed for query %s", query_buf.data);
-
-	proc = SPI_processed;
-	/* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */
-	tuptable = SPI_tuptable;
-	spi_tupdesc = tuptable->tupdesc;
-
-/* Switch out of SPI context */
-	MemoryContextSwitchTo(oldcontext);
-
-
-/* Check that SPI returned correct result. If you put a comma into one of
- * the function parameters, this will catch it when the SPI query returns
- * e.g. 3 columns.
- */
-
-	if (spi_tupdesc->natts != 2)
-	{
-		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						errmsg("expression returning multiple columns is not valid in parameter list"),
-						errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts)));
-	}
-
-/* Setup the parser. Beware that this must happen in the same context as the
- * cleanup - which means that any error from here on must do cleanup to
- * ensure that the entity table doesn't get freed by being out of context.
- */
-	pgxml_parser_init();
-
-	/* For each row i.e. document returned from SPI */
-	for (i = 0; i < proc; i++)
-	{
-		char	   *pkey;
-		char	   *xmldoc;
-
-		xmlDocPtr	doctree;
-		xmlXPathContextPtr ctxt;
-		xmlXPathObjectPtr res;
-		xmlChar    *resstr;
-
-
-		xmlXPathCompExprPtr comppath;
-
-		/* Extract the row data as C Strings */
-		spi_tuple = tuptable->vals[i];
-		pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
-		xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
-
-		/*
-		 * Clear the values array, so that not-well-formed documents return
-		 * NULL in all columns.
-		 */
-
-		/* Note that this also means that spare columns will be NULL. */
-		for (j = 0; j < ret_tupdesc->natts; j++)
-			values[j] = NULL;
-
-		/* Insert primary key */
-		values[0] = pkey;
-
-		/* Parse the document */
-		if (xmldoc)
-			doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
-		else	/* treat NULL as not well-formed */
-			doctree = NULL;
-
-		if (doctree == NULL)
-		{
-			/* not well-formed, so output all-NULL tuple */
-			ret_tuple = BuildTupleFromCStrings(attinmeta, values);
-			tuplestore_puttuple(tupstore, ret_tuple);
-			heap_freetuple(ret_tuple);
-		}
-		else
-		{
-			/* New loop here - we have to deal with nodeset results */
-			rownr = 0;
-
-			do
-			{
-				/* Now evaluate the set of xpaths. */
-				had_values = 0;
-				for (j = 0; j < numpaths; j++)
-				{
-
-					ctxt = xmlXPathNewContext(doctree);
-					ctxt->node = xmlDocGetRootElement(doctree);
-					xmlSetGenericErrorFunc(ctxt, pgxml_errorHandler);
-
-					/* compile the path */
-					comppath = xmlXPathCompile(xpaths[j]);
-					if (comppath == NULL)
-					{
-						xmlCleanupParser();
-						xmlFreeDoc(doctree);
-
-						elog_error(ERROR, "XPath Syntax Error", 1);
-
-						PG_RETURN_NULL();		/* Keep compiler happy */
-					}
-
-					/* Now evaluate the path expression. */
-					res = xmlXPathCompiledEval(comppath, ctxt);
-					xmlXPathFreeCompExpr(comppath);
-
-					if (res != NULL)
-					{
-						switch (res->type)
-						{
-							case XPATH_NODESET:
-								/* We see if this nodeset has enough nodes */
-								if ((res->nodesetval != NULL) && (rownr < res->nodesetval->nodeNr))
-								{
-									resstr =
-										xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
-									had_values = 1;
-								}
-								else
-									resstr = NULL;
-
-								break;
-
-							case XPATH_STRING:
-								resstr = xmlStrdup(res->stringval);
-								break;
-
-							default:
-								elog(NOTICE, "unsupported XQuery result: %d", res->type);
-								resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
-						}
-
-
-						/*
-						 * Insert this into the appropriate column in the
-						 * result tuple.
-						 */
-						values[j + 1] = (char *) resstr;
-					}
-					xmlXPathFreeContext(ctxt);
-				}
-				/* Now add the tuple to the output, if there is one. */
-				if (had_values)
-				{
-					ret_tuple = BuildTupleFromCStrings(attinmeta, values);
-					tuplestore_puttuple(tupstore, ret_tuple);
-					heap_freetuple(ret_tuple);
-				}
-
-				rownr++;
-
-			} while (had_values);
-
-		}
-
-		xmlFreeDoc(doctree);
-
-		if (pkey)
-			pfree(pkey);
-		if (xmldoc)
-			pfree(xmldoc);
-	}
-
-	xmlCleanupParser();
-/* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */
-	tuplestore_donestoring(tupstore);
-
-	SPI_finish();
-
-	rsinfo->setResult = tupstore;
-
-	/*
-	 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
-	 * tuples are in our tuplestore and passed back through rsinfo->setResult.
-	 * rsinfo->setDesc is set to the tuple description that we actually used
-	 * to build our tuples with, so the caller can verify we did what it was
-	 * expecting.
-	 */
-	return (Datum) 0;
-
-}
diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c
deleted file mode 100644
index 798e688..0000000
--- a/contrib/xml2/xslt_proc.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * $PostgreSQL$
- *
- * XSLT processing functions (requiring libxslt)
- *
- * John Gray, for Torchbox 2003-04-01
- */
-#include "postgres.h"
-
-#include "executor/spi.h"
-#include "fmgr.h"
-#include "funcapi.h"
-#include "miscadmin.h"
-#include "utils/builtins.h"
-
-/* libxml includes */
-
-#include <libxml/xpath.h>
-#include <libxml/tree.h>
-#include <libxml/xmlmemory.h>
-
-/* libxslt includes */
-
-#include <libxslt/xslt.h>
-#include <libxslt/xsltInternals.h>
-#include <libxslt/transform.h>
-#include <libxslt/xsltutils.h>
-
-
-/* declarations to come from xpath.c */
-extern void elog_error(int level, char *explain, int force);
-extern void pgxml_parser_init();
-extern xmlChar *pgxml_texttoxmlchar(text *textstring);
-
-/* local defs */
-static void parse_params(const char **params, text *paramstr);
-
-Datum		xslt_process(PG_FUNCTION_ARGS);
-
-
-#define MAXPARAMS 20			/* must be even, see parse_params() */
-
-
-PG_FUNCTION_INFO_V1(xslt_process);
-
-Datum
-xslt_process(PG_FUNCTION_ARGS)
-{
-	text	   *doct = PG_GETARG_TEXT_P(0);
-	text	   *ssheet = PG_GETARG_TEXT_P(1);
-	text	   *paramstr;
-	const char *params[MAXPARAMS + 1];	/* +1 for the terminator */
-	xsltStylesheetPtr stylesheet = NULL;
-	xmlDocPtr	doctree;
-	xmlDocPtr	restree;
-	xmlDocPtr	ssdoc = NULL;
-	xmlChar    *resstr;
-	int			resstat;
-	int			reslen;
-
-	if (fcinfo->nargs == 3)
-	{
-		paramstr = PG_GETARG_TEXT_P(2);
-		parse_params(params, paramstr);
-	}
-	else
-		/* No parameters */
-		params[0] = NULL;
-
-	/* Setup parser */
-	pgxml_parser_init();
-
-	/* Check to see if document is a file or a literal */
-
-	if (VARDATA(doct)[0] == '<')
-		doctree = xmlParseMemory((char *) VARDATA(doct), VARSIZE(doct) - VARHDRSZ);
-	else
-		doctree = xmlParseFile(text_to_cstring(doct));
-
-	if (doctree == NULL)
-	{
-		xmlCleanupParser();
-		elog_error(ERROR, "error parsing XML document", 0);
-
-		PG_RETURN_NULL();
-	}
-
-	/* Same for stylesheet */
-	if (VARDATA(ssheet)[0] == '<')
-	{
-		ssdoc = xmlParseMemory((char *) VARDATA(ssheet),
-							   VARSIZE(ssheet) - VARHDRSZ);
-		if (ssdoc == NULL)
-		{
-			xmlFreeDoc(doctree);
-			xmlCleanupParser();
-			elog_error(ERROR, "error parsing stylesheet as XML document", 0);
-			PG_RETURN_NULL();
-		}
-
-		stylesheet = xsltParseStylesheetDoc(ssdoc);
-	}
-	else
-		stylesheet = xsltParseStylesheetFile((xmlChar *) text_to_cstring(ssheet));
-
-
-	if (stylesheet == NULL)
-	{
-		xmlFreeDoc(doctree);
-		xsltCleanupGlobals();
-		xmlCleanupParser();
-		elog_error(ERROR, "failed to parse stylesheet", 0);
-		PG_RETURN_NULL();
-	}
-
-	restree = xsltApplyStylesheet(stylesheet, doctree, params);
-	resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet);
-
-	xsltFreeStylesheet(stylesheet);
-	xmlFreeDoc(restree);
-	xmlFreeDoc(doctree);
-
-	xsltCleanupGlobals();
-	xmlCleanupParser();
-
-	if (resstat < 0)
-		PG_RETURN_NULL();
-
-	PG_RETURN_TEXT_P(cstring_to_text_with_len((char *) resstr, reslen));
-}
-
-
-static void
-parse_params(const char **params, text *paramstr)
-{
-	char	   *pos;
-	char	   *pstr;
-	int			i;
-	char	   *nvsep = "=";
-	char	   *itsep = ",";
-
-	pstr = text_to_cstring(paramstr);
-
-	pos = pstr;
-
-	for (i = 0; i < MAXPARAMS; i++)
-	{
-		params[i] = pos;
-		pos = strstr(pos, nvsep);
-		if (pos != NULL)
-		{
-			*pos = '\0';
-			pos++;
-		}
-		else
-		{
-			/* No equal sign, so ignore this "parameter" */
-			/* We'll reset params[i] to NULL below the loop */
-			break;
-		}
-		/* Value */
-		i++;
-		/* since MAXPARAMS is even, we still have i < MAXPARAMS */
-		params[i] = pos;
-		pos = strstr(pos, itsep);
-		if (pos != NULL)
-		{
-			*pos = '\0';
-			pos++;
-		}
-		else
-		{
-			i++;
-			break;
-		}
-	}
-
-	params[i] = NULL;
-}
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index cd8775d..00b2ce5 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -117,6 +117,5 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
  &unaccent;
  &uuid-ossp;
  &vacuumlo;
- &xml2;
 
 </appendix>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 4d2a0d7..204daba 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -130,7 +130,6 @@
 <!entity unaccent      SYSTEM "unaccent.sgml">
 <!entity uuid-ossp       SYSTEM "uuid-ossp.sgml">
 <!entity vacuumlo        SYSTEM "vacuumlo.sgml">
-<!entity xml2            SYSTEM "xml2.sgml"> 
 
 <!-- appendixes -->
 <!entity contacts   SYSTEM "contacts.sgml">
diff --git a/doc/src/sgml/install-win32.sgml b/doc/src/sgml/install-win32.sgml
index a6b97f4..28814be 100644
--- a/doc/src/sgml/install-win32.sgml
+++ b/doc/src/sgml/install-win32.sgml
@@ -212,8 +212,7 @@
     </varlistentry>
 
     <varlistentry>
-     <term><productname>libxml2</productname> and
-      <productname>libxslt</productname></term>
+     <term><productname>libxml2</productname></term>
      <listitem><para>
       Required for XML support. Binaries can be downloaded from
       <ulink url="http://zlatkovic.com/pub/libxml"></> or source from
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 30041ad..4d5a427 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1027,17 +1027,6 @@ su - postgres
       </varlistentry>
 
       <varlistentry>
-       <term><option>--with-libxslt</option></term>
-       <listitem>
-        <para>
-         Use libxslt when building <filename>contrib/xml2</>.
-         <filename>contrib/xml2</> relies on this library to perform
-         XSL transformations of XML.
-        </para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
        <term><option>--disable-integer-datetimes</option></term>
        <listitem>
         <para>
diff --git a/doc/src/sgml/xml2.sgml b/doc/src/sgml/xml2.sgml
deleted file mode 100644
index 3507684..0000000
--- a/doc/src/sgml/xml2.sgml
+++ /dev/null
@@ -1,425 +0,0 @@
-<!-- $PostgreSQL$ -->
-
-<sect1 id="xml2">
- <title>xml2</title>
-
- <indexterm zone="xml2">
-  <primary>xml2</primary>
- </indexterm>
-
- <para>
-  The <filename>xml2</> module provides XPath querying and
-  XSLT functionality.
- </para>
-
- <sect2>
-  <title>Deprecation notice</title>
-
-  <para>
-   From <productname>PostgreSQL</> 8.3 on, there is XML-related
-   functionality based on the SQL/XML standard in the core server.
-   That functionality covers XML syntax checking and XPath queries,
-   which is what this module does, and more, but the API is
-   not at all compatible.  It is planned that this module will be
-   removed in PostgreSQL 8.4 in favor of the newer standard API, so
-   you are encouraged to try converting your applications.  If you
-   find that some of the functionality of this module is not
-   available in an adequate form with the newer API, please explain
-   your issue to pgsql-hackers@postgresql.org so that the deficiency
-   can be addressed.
-  </para>
- </sect2>
-
- <sect2>
-  <title>Description of functions</title>
-
-  <para>
-   <xref linkend="xml2-functions-table"> shows the functions provided by this module.
-   These functions provide straightforward XML parsing and XPath queries.
-   All arguments are of type <type>text</>, so for brevity that is not shown.
-  </para>
-
-  <table id="xml2-functions-table">
-   <title>Functions</title>
-   <tgroup cols="2">
-    <tbody>
-     <row>
-      <entry>
-       <synopsis>
-        xml_is_well_formed(document) returns bool
-       </synopsis>
-      </entry>
-      <entry>
-       <para>
-        This parses the document text in its parameter and returns true if the
-        document is well-formed XML.  (Note: before PostgreSQL 8.2, this
-        function was called <function>xml_valid()</>.  That is the wrong name
-        since validity and well-formedness have different meanings in XML.
-        The old name is still available, but is deprecated.)
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry>
-       <synopsis>
-        xpath_string(document,query) returns text
-        xpath_number(document,query) returns float4
-        xpath_bool(document,query) returns bool
-       </synopsis>
-      </entry>
-      <entry>
-       <para>
-        These functions evaluate the XPath query on the supplied document, and
-        cast the result to the specified type.
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry>
-       <synopsis>
-        xpath_nodeset(document,query,toptag,itemtag) returns text
-       </synopsis>
-      </entry>
-      <entry>
-       <para>
-       This evaluates query on document and wraps the result in XML tags. If
-       the result is multivalued, the output will look like:
-       </para>
-       <literal>
-        &lt;toptag&gt;
-        &lt;itemtag&gt;Value 1 which could be an XML fragment&lt;/itemtag&gt;
-        &lt;itemtag&gt;Value 2....&lt;/itemtag&gt;
-        &lt;/toptag&gt;
-       </literal>
-       <para>
-        If either toptag or itemtag is an empty string, the relevant tag is omitted.
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry>
-       <synopsis>
-        xpath_nodeset(document,query) returns text
-       </synopsis>
-      </entry>
-      <entry>
-       <para>
-        Like xpath_nodeset(document,query,toptag,itemtag) but result omits both tags.
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry>
-       <synopsis>
-        xpath_nodeset(document,query,itemtag) returns text
-       </synopsis>
-      </entry>
-      <entry>
-       <para>
-        Like xpath_nodeset(document,query,toptag,itemtag) but result omits toptag.
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry>
-       <synopsis>
-        xpath_list(document,query,separator) returns text
-       </synopsis>
-      </entry>
-      <entry>
-       <para>
-        This function returns multiple values separated by the specified
-        separator, for example <literal>Value 1,Value 2,Value 3</> if
-        separator is <literal>,</>.
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry>
-       <synopsis>
-        xpath_list(document,query) returns text
-       </synopsis>
-      </entry>
-      <entry>
-       This is a wrapper for the above function that uses <literal>,</>
-       as the separator.
-      </entry>
-     </row>
-    </tbody>
-   </tgroup>
-  </table>
- </sect2>
-
- <sect2>
-  <title><literal>xpath_table</literal></title>
-
-  <synopsis>
-   xpath_table(text key, text document, text relation, text xpaths, text criteria) returns setof record
-  </synopsis>
-
-  <para>
-   <function>xpath_table</> is a table function that evaluates a set of XPath
-   queries on each of a set of documents and returns the results as a
-   table. The primary key field from the original document table is returned
-   as the first column of the result so that the result set
-   can readily be used in joins.
-  </para>
-
-  <table>
-   <title>Parameters</title>
-   <tgroup cols="2">
-    <tbody>
-     <row>
-      <entry><parameter>key</parameter></entry>
-      <entry>
-       <para>
-        the name of the <quote>key</> field &mdash; this is just a field to be used as
-        the first column of the output table, i.e., it identifies the record from
-        which each output row came (see note below about multiple values)
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry><parameter>document</parameter></entry>
-      <entry>
-       <para>
-        the name of the field containing the XML document
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry><parameter>relation</parameter></entry>
-      <entry>
-       <para>
-        the name of the table or view containing the documents
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry><parameter>xpaths</parameter></entry>
-      <entry>
-       <para>
-        one or more XPath expressions, separated by <literal>|</literal>
-       </para>
-      </entry>
-     </row>
-     <row>
-      <entry><parameter>criteria</parameter></entry>
-      <entry>
-       <para>
-        the contents of the WHERE clause. This cannot be omitted, so use
-        <literal>true</literal> or <literal>1=1</literal> if you want to
-        process all the rows in the relation
-       </para>
-      </entry>
-     </row>
-    </tbody>
-   </tgroup>
-  </table>
-
-  <para>
-   These parameters (except the XPath strings) are just substituted
-   into a plain SQL SELECT statement, so you have some flexibility &mdash; the
-   statement is
-  </para>
-
-  <para>
-   <literal>
-    SELECT &lt;key&gt;, &lt;document&gt; FROM &lt;relation&gt; WHERE &lt;criteria&gt;
-   </literal>
-  </para>
-
-  <para>
-   so those parameters can be <emphasis>anything</> valid in those particular
-   locations. The result from this SELECT needs to return exactly two
-   columns (which it will unless you try to list multiple fields for key
-   or document). Beware that this simplistic approach requires that you
-   validate any user-supplied values to avoid SQL injection attacks.
-  </para>
-
-  <para>
-   The function has to be used in a <literal>FROM</> expression, with an
-   <literal>AS</> clause to specify the output columns; for example
-  </para>
-
-  <programlisting>
-SELECT * FROM
-xpath_table('article_id',
-            'article_xml',
-            'articles',
-            '/article/author|/article/pages|/article/title',
-            'date_entered > ''2003-01-01'' ')
-AS t(article_id integer, author text, page_count integer, title text);
-  </programlisting>
-
-  <para>
-   The <literal>AS</> clause defines the names and types of the columns in the
-   output table.  The first is the <quote>key</> field and the rest correspond
-   to the XPath queries.
-   If there are more XPath queries than result columns,
-   the extra queries will be ignored. If there are more result columns
-   than XPath queries, the extra columns will be NULL.
-  </para>
-
-  <para>
-   Notice that this example defines the <structname>page_count</> result
-   column as an integer.  The function deals internally with string
-   representations, so when you say you want an integer in the output, it will
-   take the string representation of the XPath result and use PostgreSQL input
-   functions to transform it into an integer (or whatever type the <type>AS</>
-   clause requests). An error will result if it can't do this &mdash; for
-   example if the result is empty &mdash; so you may wish to just stick to
-   <type>text</> as the column type if you think your data has any problems.
-  </para>
-
-  <para>
-   The calling <command>SELECT</> statement doesn't necessarily have be
-   be just <literal>SELECT *</> &mdash; it can reference the output
-   columns by name or join them to other tables. The function produces a
-   virtual table with which you can perform any operation you wish (e.g.
-   aggregation, joining, sorting etc). So we could also have:
-  </para>
-
-  <programlisting>
-SELECT t.title, p.fullname, p.email
-FROM xpath_table('article_id', 'article_xml', 'articles',
-                 '/article/title|/article/author/@id',
-                 'xpath_string(article_xml,''/article/@date'') > ''2003-03-20'' ')
-       AS t(article_id integer, title text, author_id integer),
-     tblPeopleInfo AS p
-WHERE t.author_id = p.person_id;
-  </programlisting>
-
-  <para>
-   as a more complicated example. Of course, you could wrap all
-   of this in a view for convenience.
-  </para>
-
-  <sect3>
-   <title>Multivalued results</title>
-
-   <para>
-    The <function>xpath_table</> function assumes that the results of each XPath query
-    might be multi-valued, so the number of rows returned by the function
-    may not be the same as the number of input documents. The first row
-    returned contains the first result from each query, the second row the
-    second result from each query. If one of the queries has fewer values
-    than the others, NULLs will be returned instead.
-   </para>
-
-   <para>
-    In some cases, a user will know that a given XPath query will return
-    only a single result (perhaps a unique document identifier) &mdash; if used
-    alongside an XPath query returning multiple results, the single-valued
-    result will appear only on the first row of the result. The solution
-    to this is to use the key field as part of a join against a simpler
-    XPath query. As an example:
-   </para>
-
-   <programlisting>
-    CREATE TABLE test (
-        id int4 NOT NULL,
-        xml text,
-        CONSTRAINT pk PRIMARY KEY (id)
-    );
-
-    INSERT INTO test VALUES (1, '&lt;doc num="C1"&gt;
-    &lt;line num="L1"&gt;&lt;a&gt;1&lt;/a&gt;&lt;b&gt;2&lt;/b&gt;&lt;c&gt;3&lt;/c&gt;&lt;/line&gt;
-    &lt;line num="L2"&gt;&lt;a&gt;11&lt;/a&gt;&lt;b&gt;22&lt;/b&gt;&lt;c&gt;33&lt;/c&gt;&lt;/line&gt;
-    &lt;/doc&gt;');
-
-    INSERT INTO test VALUES (2, '&lt;doc num="C2"&gt;
-    &lt;line num="L1"&gt;&lt;a&gt;111&lt;/a&gt;&lt;b&gt;222&lt;/b&gt;&lt;c&gt;333&lt;/c&gt;&lt;/line&gt;
-    &lt;line num="L2"&gt;&lt;a&gt;111&lt;/a&gt;&lt;b&gt;222&lt;/b&gt;&lt;c&gt;333&lt;/c&gt;&lt;/line&gt;
-    &lt;/doc&gt;');
-
-    SELECT * FROM
-      xpath_table('id','xml','test',
-                  '/doc/@num|/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c',
-                  'true')
-      AS t(id int4, doc_num varchar(10), line_num varchar(10), val1 int4, val2 int4, val3 int4)
-    WHERE id = 1 ORDER BY doc_num, line_num
-
-     id | doc_num | line_num | val1 | val2 | val3
-    ----+---------+----------+------+------+------
-      1 | C1      | L1       |    1 |    2 |    3
-      1 |         | L2       |   11 |   22 |   33
-   </programlisting>
-
-   <para>
-    To get doc_num on every line, the solution is to use two invocations
-    of xpath_table and join the results:
-   </para>
-
-   <programlisting>
-   SELECT t.*,i.doc_num FROM
-     xpath_table('id', 'xml', 'test',
-                 '/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c',
-                 'true')
-       AS t(id int4, line_num varchar(10), val1 int4, val2 int4, val3 int4),
-     xpath_table('id', 'xml', 'test', '/doc/@num', 'true')
-       AS i(id int4, doc_num varchar(10))
-   WHERE i.id=t.id AND i.id=1
-   ORDER BY doc_num, line_num;
-
-    id | line_num | val1 | val2 | val3 | doc_num
-   ----+----------+------+------+------+---------
-     1 | L1       |    1 |    2 |    3 | C1
-     1 | L2       |   11 |   22 |   33 | C1
-   (2 rows)
-   </programlisting>
-  </sect3>
- </sect2>
-
- <sect2>
-  <title>XSLT functions</title>
-
-  <para>
-   The following functions are available if libxslt is installed:
-  </para>
-
-  <sect3>
-   <title><literal>xslt_process</literal></title>
-
-   <synopsis>
-    xslt_process(text document, text stylesheet, text paramlist) returns text
-   </synopsis>
-
-   <para>
-    This function appplies the XSL stylesheet to the document and returns
-    the transformed result. The paramlist is a list of parameter
-    assignments to be used in the transformation, specified in the form
-    <literal>a=1,b=2</>. Note that the
-    parameter parsing is very simple-minded: parameter values cannot
-    contain commas!
-   </para>
-
-   <para>
-    Also note that if either the document or stylesheet values do not
-    begin with a &lt; then they will be treated as URLs and libxslt will
-    fetch them. It follows that you can use <function>xslt_process</> as a
-    means to fetch the contents of URLs &mdash; you should be aware of the
-    security implications of this.
-   </para>
-
-   <para>
-    There is also a two-parameter version of <function>xslt_process</> which
-    does not pass any parameters to the transformation.
-   </para>
-  </sect3>
- </sect2>
-
- <sect2>
-  <title>Author</title>
-
-  <para>
-   John Gray <email>jgray@azuli.co.uk</email>
-  </para>
-
-  <para>
-   Development of this module was sponsored by Torchbox Ltd. (www.torchbox.com).
-   It has the same BSD licence as PostgreSQL.
-  </para>
- </sect2>
-
-</sect1>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 7a6e3a9..813a824 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -155,7 +155,6 @@ with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
 with_ossp_uuid	= @with_ossp_uuid@
 with_libxml	= @with_libxml@
-with_libxslt	= @with_libxslt@
 with_system_tzdata = @with_system_tzdata@
 with_zlib	= @with_zlib@
 enable_shared	= @enable_shared@
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 69799a5..d851c6a 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -317,9 +317,6 @@
 /* Define to 1 if you have the `xml2' library (-lxml2). */
 #undef HAVE_LIBXML2
 
-/* Define to 1 if you have the `xslt' library (-lxslt). */
-#undef HAVE_LIBXSLT
-
 /* Define to 1 if you have the `z' library (-lz). */
 #undef HAVE_LIBZ
 
@@ -759,10 +756,6 @@
 /* Define to 1 to build with XML support. (--with-libxml) */
 #undef USE_LIBXML
 
-/* Define to 1 to use XSLT support when building contrib/xml2.
-   (--with-libxslt) */
-#undef USE_LIBXSLT
-
 /* Define to select named POSIX semaphores. */
 #undef USE_NAMED_POSIX_SEMAPHORES
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index b0164bb..0d9fbb4 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -286,13 +286,11 @@ sub mkvcbuild
     {
         $contrib_extraincludes->{'pgxml'} = [
             $solution->{options}->{xml} . '\include',
-            $solution->{options}->{xslt} . '\include',
             $solution->{options}->{iconv} . '\include'
         ];
 
         $contrib_extralibs->{'pgxml'} = [
-            $solution->{options}->{xml} . '\lib\libxml2.lib',
-            $solution->{options}->{xslt} . '\lib\libxslt.lib'
+            $solution->{options}->{xml} . '\lib\libxml2.lib'
         ];
     }
     else
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 850ad5a..801874c 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -29,9 +29,9 @@ sub new
         unless exists $options->{float4byval};
     if ($options->{xml})
     {
-        if (!($options->{xslt} && $options->{iconv}))
+        if (! $options->{iconv})
         {
-            die "XML requires both XSLT and ICONV\n";
+            die "XML requires ICONV\n";
         }
     }
 	$options->{blocksize} = 8
@@ -515,7 +515,6 @@ sub GetFakeConfigure
     $cfg .= ' --with-openssl' if ($self->{options}->{ssl});
     $cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid});
     $cfg .= ' --with-libxml' if ($self->{options}->{xml});
-    $cfg .= ' --with-libxslt' if ($self->{options}->{xslt});
     $cfg .= ' --with-krb5' if ($self->{options}->{krb5});
     $cfg .= ' --with-tcl' if ($self->{options}->{tcl});
     $cfg .= ' --with-perl' if ($self->{options}->{perl});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index eea4a70..9677b52 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -19,7 +19,6 @@ our $config = {
     openssl=>undef,			# --with-ssl=<path>
     uuid=>undef,			# --with-ossp-uuid
     xml=>undef,				# --with-libxml=<path>
-    xslt=>undef,			# --with-libxslt=<path>
     iconv=>undef,			# (not in configure, path to iconv)
     zlib=>undef				# --with-zlib=<path>
 };
#2Andrew Dunstan
andrew@dunslane.net
In reply to: Robert Haas (#1)
Re: remove contrib/xml2

Robert Haas wrote:

There has been some more discussion lately of problems caused by contrib/xml2.

http://archives.postgresql.org/pgsql-bugs/2010-01/msg00251.php
http://archives.postgresql.org/pgsql-bugs/2010-01/msg00198.php

I think we need to either (1) fix the bugs and update the
documentation to remove the statement that this will be removed or (2)
actually remove it. Nobody seems interested in #1, so PFA a patch to
do #2. It also rips out all the libxslt stuff, which seems to exist
only for the purpose of supporting contrib/xml2.

The problem is that there are people who use the XSLT and xpath_table
stuff on text data and so don't run into these bugs.

I agree it's a mess but I don't think just abandoning the functionality
is a good idea.

cheers

andrew

#3Mike Rylander
mrylander@gmail.com
In reply to: Andrew Dunstan (#2)
1 attachment(s)
Re: remove contrib/xml2

On Thu, Jan 28, 2010 at 4:18 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Robert Haas wrote:

There has been some more discussion lately of problems caused by
contrib/xml2.

http://archives.postgresql.org/pgsql-bugs/2010-01/msg00251.php
http://archives.postgresql.org/pgsql-bugs/2010-01/msg00198.php

I think we need to either (1) fix the bugs and update the
documentation to remove the statement that this will be removed or (2)
actually remove it.  Nobody seems interested in #1, so PFA a patch to
do #2.  It also rips out all the libxslt stuff, which seems to exist
only for the purpose of supporting contrib/xml2.

The problem is that there are people who use the XSLT and xpath_table stuff
on text data and so don't run into these bugs.

I agree it's a mess but I don't think just abandoning the functionality is a
good idea.

I'm one of those people. :)

Expecting to see contrib/xml2 go away at some point, possibly without
replacements for xslt_process and xpath_table, I've been working on
some plpgsql and plperlu work-alikes targeted at TEXT columns, as the
xml2 versions do. I hope these (attached) will be of some help to
others. Note, these are not the exact functions I use, they are
lightly edited to remove the use of wrappers I've created to paper
over the transition from xpath_nodeset() to core XPATH().

--
Mike Rylander
| VP, Research and Design
| Equinox Software, Inc. / The Evergreen Experts
| phone: 1-877-OPEN-ILS (673-6457)
| email: miker@esilibrary.com
| web: http://www.esilibrary.com

Attachments:

xml2-replacements.sqlapplication/octet-stream; name=xml2-replacements.sqlDownload
#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#2)
Re: remove contrib/xml2

Andrew Dunstan <andrew@dunslane.net> writes:

Robert Haas wrote:

I think we need to either (1) fix the bugs and update the
documentation to remove the statement that this will be removed or (2)
actually remove it.

I agree it's a mess but I don't think just abandoning the functionality
is a good idea.

Yeah, we can't really remove it until we have a plausible substitute for
the xpath_table functionality. This is in the TODO list ...

regards, tom lane

#5Josh Berkus
josh@agliodbs.com
In reply to: Tom Lane (#4)
Re: remove contrib/xml2

Yeah, we can't really remove it until we have a plausible substitute for
the xpath_table functionality. This is in the TODO list ...

What about moving it to pgfoundry?

I'm really not keen on shipping known-broken stuff in /contrib.

--Josh Berkus

#6Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#5)
Re: remove contrib/xml2

On Thu, Jan 28, 2010 at 5:54 PM, Josh Berkus <josh@agliodbs.com> wrote:

Yeah, we can't really remove it until we have a plausible substitute for
the xpath_table functionality.  This is in the TODO list ...

What about moving it to pgfoundry?

I'm really not keen on shipping known-broken stuff in /contrib.

Yeah, exactly. Another option - if we know that it works OK with
inputs of certain types - might be to throw an error if we get an
input of a type we know it doesn't work with. But I guess I haven't
followed this closely enough to know exactly what kinds of arguments
do/don't work.

...Robert

#7Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#6)
Re: remove contrib/xml2

Robert Haas escribi�:

On Thu, Jan 28, 2010 at 5:54 PM, Josh Berkus <josh@agliodbs.com> wrote:

Yeah, we can't really remove it until we have a plausible substitute for
the xpath_table functionality. �This is in the TODO list ...

What about moving it to pgfoundry?

I'm really not keen on shipping known-broken stuff in /contrib.

Yeah, exactly. Another option - if we know that it works OK with
inputs of certain types - might be to throw an error if we get an
input of a type we know it doesn't work with. But I guess I haven't
followed this closely enough to know exactly what kinds of arguments
do/don't work.

If it were easy to throw an error, it'd be better to avoid the crash.

--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

#8Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#4)
Re: remove contrib/xml2

On Thu, Jan 28, 2010 at 5:41 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

Robert Haas wrote:

I think we need to either (1) fix the bugs and update the
documentation to remove the statement that this will be removed or (2)
actually remove it.

I agree it's a mess but I don't think just abandoning the functionality
is a good idea.

Yeah, we can't really remove it until we have a plausible substitute for
the xpath_table functionality.  This is in the TODO list ...

My feeling is that if it's as flakey and unreliable as it currently
is, we shouldn't ship it. Removing it from CVS doesn't mean "you
can't use this any more"; this is open source. It just means people
will have to go and get an old copy out of CVS and presumably in so
doing they will be aware that we've removed it for a reason. We have
a well-deserved reputation for quality and I would like to see us
preserve that.

Is anyone going to try to fix this for 9.0?

...Robert

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#8)
Re: remove contrib/xml2

Robert Haas <robertmhaas@gmail.com> writes:

My feeling is that if it's as flakey and unreliable as it currently
is, we shouldn't ship it. Removing it from CVS doesn't mean "you
can't use this any more"; this is open source. It just means people
will have to go and get an old copy out of CVS and presumably in so
doing they will be aware that we've removed it for a reason. We have
a well-deserved reputation for quality and I would like to see us
preserve that.

[ shrug... ] It is not any more flaky than it's been since it was put in.
The people who have been depending on it presumably have use-patterns
for which it doesn't fail, and we're not going to be doing them a
service by ripping out functionality for which we can't offer a
replacement.

As for the "quality" argument, contrib modules are not guaranteed
to be of the same standard as the core code; anyone who thinks they are
should disabuse themselves of the notion by reading some of that code.

regards, tom lane

#10Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#9)
Re: remove contrib/xml2

On Mon, Feb 1, 2010 at 1:38 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

My feeling is that if it's as flakey and unreliable as it currently
is, we shouldn't ship it.  Removing it from CVS doesn't mean "you
can't use this any more"; this is open source.  It just means people
will have to go and get an old copy out of CVS and presumably in so
doing they will be aware that we've removed it for a reason.  We have
a well-deserved reputation for quality and I would like to see us
preserve that.

[ shrug... ]  It is not any more flaky than it's been since it was put in.
The people who have been depending on it presumably have use-patterns
for which it doesn't fail, and we're not going to be doing them a
service by ripping out functionality for which we can't offer a
replacement.

Well, then we'd at least better update the documentation to (1) remove
the statement that this will be removed in 8.4 (since we didn't), and
(2) add a very, very large warning that this will crash if you do
almost anything with it.

...Robert

#11Andrew Dunstan
andrew@dunslane.net
In reply to: Robert Haas (#10)
Re: remove contrib/xml2

Robert Haas wrote:

(2) add a very, very large warning that this will crash if you do
almost anything with it.

I think that's an exaggeration. Certain people are known to be using it
quite successfully.

cheers

andrew

#12Robert Haas
robertmhaas@gmail.com
In reply to: Andrew Dunstan (#11)
Re: remove contrib/xml2

On Mon, Feb 1, 2010 at 5:23 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Robert Haas wrote:

(2) add a very, very large warning that this will crash if you do
almost anything with it.

I think that's an exaggeration. Certain people are known to be using it
quite successfully.

Hmm. Well, all I know is that the first thing I tried crashed the server.

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

It doesn't crash if you change the type of t from xml to text; instead
you get a warning about some sort of memory allocation problem.

DROP TABLE xpath_test;
CREATE TABLE xpath_test (id integer NOT NULL, t text);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

yields:

WARNING: problem in alloc set ExprContext: bogus aset link in block
0x14645e0, chunk 0x14648b8

And then there's this (see also bug #5285):

DELETE FROM xpath_test;
INSERT INTO xpath_test VALUES (1, '<rowlist><row a="1"/><row a="2"
b="oops"/></rowlist>');
SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row/@a|/rowlist/row/@b', 'true') as t(id int4, a text, b
text);

which yields an answer that is, at least, extremely surprising, if not
flat-out wrong:

id | a | b
----+---+------
1 | 1 | oops
1 | 2 |
(2 rows)

Bugs #4953 and #5079 can also be reproduced in CVS HEAD. Both crash the server.

...Robert

#13Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#12)
Re: remove contrib/xml2

Robert Haas escribi�:

On Mon, Feb 1, 2010 at 5:23 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Robert Haas wrote:

(2) add a very, very large warning that this will crash if you do
almost anything with it.

I think that's an exaggeration. Certain people are known to be using it
quite successfully.

Hmm. Well, all I know is that the first thing I tried crashed the server.

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

This trivial patch lingering on my system fixes this crasher (this is
for the 8.3 branch). It makes the "problem in alloc set ExprContext"
warning show up instead.

There are still lotsa other holes, but hey, this is a start ...

Index: contrib/xml2/xpath.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/contrib/xml2/xpath.c,v
retrieving revision 1.16.2.1
diff -c -p -r1.16.2.1 xpath.c
*** contrib/xml2/xpath.c	26 Mar 2008 01:19:11 -0000	1.16.2.1
--- contrib/xml2/xpath.c	27 Jan 2010 15:30:56 -0000
*************** xpath_table(PG_FUNCTION_ARGS)
*** 793,798 ****
--- 793,801 ----
   */
  	pgxml_parser_init();
+ 	PG_TRY();
+ 	{
+ 
  	/* For each row i.e. document returned from SPI */
  	for (i = 0; i < proc; i++)
  	{
*************** xpath_table(PG_FUNCTION_ARGS)
*** 929,934 ****
--- 932,944 ----
  		if (xmldoc)
  			pfree(xmldoc);
  	}
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		xmlCleanupParser();
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();

xmlCleanupParser();
/* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */

--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

#14M Z
jm80008@gmail.com
In reply to: Robert Haas (#12)
Re: remove contrib/xml2

I did some tests followed Robert's test cases on both postgresql 8.4.2-0ubu
and 8.3.8-1, OS: Ubuntu Karmic.

1) 1st test case, it doesn't crash on 8.3.8 but crash on 8.4.2;
2) 2nd test case, both 8.3.8 and 8.4.2 are fine, and no warning (different
from Robert's test?);
3) 3rd test case (and modified test case for 8.3.8), both 8.3.8 and 8.4.2
are not correct, same with Robert's test (8.5 beta?);

*************************************
1st test case:
==================
8.3.8
==================
conifer=# CREATE TABLE xpath_test (id integer NOT NULL, t xml);
CREATE TABLE
conifer=# INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
INSERT 0 1
conifer=# SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int',
'true') as t(id int4);
id
----
1
(1 row)

==================
8.4.2
==================
conifer=# CREATE TABLE xpath_test (id integer NOT NULL, t xml);
CREATE TABLE
conifer=# INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
INSERT 0 1
conifer=# SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int',
'true') as t(id int4);
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
!>
*************************************

*************************************
2nd test case
==================
8.3.8 and 8.4.2
==================
conifer=# CREATE TABLE xpath_test (id integer NOT NULL, t text);
CREATE TABLE
conifer=# INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
INSERT 0 1
conifer=# SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int',
'true') as t(id int4);
id
----
1
(1 row)
*************************************

*************************************
3rd test case
==================
8.3.8 and 8.4.2
==================
conifer=# CREATE TABLE xpath_test (id integer NOT NULL, t text);
CREATE TABLE
conifer=# INSERT INTO xpath_test VALUES (1, '<rowlist><row a="1"/><row a="2"
b="oops"/></rowlist>');
INSERT 0 1
conifer=# SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row/@a|/rowlist/row/@b', 'true') as t(id int4, a text, b text);
id | a | b
----+---+------
1 | 1 | oops
1 | 2 |
(2 rows)

==================
8.3.8 (modified 3rd test case, because 8.3.8 won't crash using xml)
==================
conifer=# CREATE TABLE xpath_test (id integer NOT NULL, t xml);
CREATE TABLE
conifer=# INSERT INTO xpath_test VALUES (1, '<rowlist><row a="1"/><row a="2"
b="oops"/></rowlist>');
INSERT 0 1
conifer=# SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row/@a|/rowlist/row/@b', 'true') as t(id int4, a text, b text);
id | a | b
----+---+------
1 | 1 | oops
1 | 2 |
(2 rows)
*************************************

For 1st test case, not sure if some paths applied to 8.3 haven't been
applied to 8.4, or other reasons cause the difference between 8.3.8 and
8.4.2.

Any ideas or comments?

Thank you,
M Z

On Mon, Feb 1, 2010 at 8:44 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Show quoted text

On Mon, Feb 1, 2010 at 5:23 PM, Andrew Dunstan <andrew@dunslane.net>
wrote:

Robert Haas wrote:

(2) add a very, very large warning that this will crash if you do
almost anything with it.

I think that's an exaggeration. Certain people are known to be using it
quite successfully.

Hmm. Well, all I know is that the first thing I tried crashed the server.

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

It doesn't crash if you change the type of t from xml to text; instead
you get a warning about some sort of memory allocation problem.

DROP TABLE xpath_test;
CREATE TABLE xpath_test (id integer NOT NULL, t text);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

yields:

WARNING: problem in alloc set ExprContext: bogus aset link in block
0x14645e0, chunk 0x14648b8

And then there's this (see also bug #5285):

DELETE FROM xpath_test;
INSERT INTO xpath_test VALUES (1, '<rowlist><row a="1"/><row a="2"
b="oops"/></rowlist>');
SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row/@a|/rowlist/row/@b', 'true') as t(id int4, a text, b
text);

which yields an answer that is, at least, extremely surprising, if not
flat-out wrong:

id | a | b
----+---+------
1 | 1 | oops
1 | 2 |
(2 rows)

Bugs #4953 and #5079 can also be reproduced in CVS HEAD. Both crash the
server.

...Robert

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Robert Haas
robertmhaas@gmail.com
In reply to: M Z (#14)
Re: remove contrib/xml2

On Thu, Feb 4, 2010 at 10:51 PM, M Z <jm80008@gmail.com> wrote:

I did some tests followed Robert's test cases on both postgresql 8.4.2-0ubu
and 8.3.8-1, OS: Ubuntu Karmic.

1) 1st test case, it doesn't crash on 8.3.8 but crash on 8.4.2;

Interesting. So, that's a regression of some kind.

2) 2nd test case, both 8.3.8 and 8.4.2 are fine, and no warning (different
from Robert's test?);

I built with --enable-debug and --enable-cassert, which might be relevant.

3) 3rd test case (and modified test case for 8.3.8), both 8.3.8 and 8.4.2
are not correct, same with Robert's test (8.5 beta?);

As I think about that further, it might not be a bug - how is the
processor supposed to know what we expect to happen? But then, I
don't really know how this is supposed to work.

...Robert

#16Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#13)
Re: remove contrib/xml2

On Wed, Feb 3, 2010 at 8:49 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Robert Haas escribió:

On Mon, Feb 1, 2010 at 5:23 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Robert Haas wrote:

(2) add a very, very large warning that this will crash if you do
almost anything with it.

I think that's an exaggeration. Certain people are known to be using it
quite successfully.

Hmm.  Well, all I know is that the first thing I tried crashed the server.

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

This trivial patch lingering on my system fixes this crasher (this is
for the 8.3 branch).  It makes the "problem in alloc set ExprContext"
warning show up instead.

There are still lotsa other holes, but hey, this is a start ...

Interestingly M Z found he couldn't reproduce this crash on 8.3. Can
you? If so, +1 for applying this and backpatching it as far as make
sense.

...Robert

#17M Z
jm80008@gmail.com
In reply to: Robert Haas (#16)
Re: remove contrib/xml2

The thing is, why it doesn't crash on 8.3.8 but crash on 8.4.2? Any idea? A
patch was applied to 8.3 but not to 8.4.2?

Thanks,
M Z

On Fri, Feb 5, 2010 at 1:45 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Show quoted text

On Wed, Feb 3, 2010 at 8:49 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Robert Haas escribió:

On Mon, Feb 1, 2010 at 5:23 PM, Andrew Dunstan <andrew@dunslane.net>

wrote:

Robert Haas wrote:

(2) add a very, very large warning that this will crash if you do
almost anything with it.

I think that's an exaggeration. Certain people are known to be using

it

quite successfully.

Hmm. Well, all I know is that the first thing I tried crashed the

server.

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

This trivial patch lingering on my system fixes this crasher (this is
for the 8.3 branch). It makes the "problem in alloc set ExprContext"
warning show up instead.

There are still lotsa other holes, but hey, this is a start ...

Interestingly M Z found he couldn't reproduce this crash on 8.3. Can
you? If so, +1 for applying this and backpatching it as far as make
sense.

...Robert

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Tom Lane
tgl@sss.pgh.pa.us
In reply to: M Z (#17)
Re: remove contrib/xml2

M Z <jm80008@gmail.com> writes:

The thing is, why it doesn't crash on 8.3.8 but crash on 8.4.2? Any idea?

Pure luck. Memory-clobber bugs like these are notoriously
nondeterministic. Any minor, logically unrelated change could make them
visible or not visible, because the clobber happens to clobber data that
is or is not in active use at the moment.

regards, tom lane

#19M Z
jm80008@gmail.com
In reply to: Alvaro Herrera (#13)
Re: remove contrib/xml2

Hi Alvaro,

I followed your instruction but put the patch on 8.4.2 as I found it
crashes. It looks like the server still crash in the same way. Can you and
anyone give me some ideas how to fix this bug?

==============================
conifer=# CREATE TABLE xpath_test (id integer NOT NULL, t xml);
CREATE TABLE
conifer=# INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
INSERT 0 1
conifer=# SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int',
'true') as t(id int4);
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
!>
==============================

Best,
M Z

Show quoted text

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<doc><int>1</int></doc>');
SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
as t(id int4);

Hmm. Well, all I know is that the first thing I tried crashed the

server.

This trivial patch lingering on my system fixes this crasher (this is
for the 8.3 branch). It makes the "problem in alloc set ExprContext"
warning show up instead.

There are still lotsa other holes, but hey, this is a start ...

Index: contrib/xml2/xpath.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/contrib/xml2/xpath.c,v
retrieving revision 1.16.2.1
diff -c -p -r1.16.2.1 xpath.c
*** contrib/xml2/xpath.c        26 Mar 2008 01:19:11 -0000      1.16.2.1
--- contrib/xml2/xpath.c        27 Jan 2010 15:30:56 -0000
*************** xpath_table(PG_FUNCTION_ARGS)
*** 793,798 ****
--- 793,801 ----
*/
pgxml_parser_init();
+       PG_TRY();
+       {
+
/* For each row i.e. document returned from SPI */
for (i = 0; i < proc; i++)
{
*************** xpath_table(PG_FUNCTION_ARGS)
*** 929,934 ****
--- 932,944 ----
if (xmldoc)
pfree(xmldoc);
}
+       }
+       PG_CATCH();
+       {
+               xmlCleanupParser();
+               PG_RE_THROW();
+       }
+       PG_END_TRY();

xmlCleanupParser();
/* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */

--
Alvaro Herrera
http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Alvaro Herrera
alvherre@commandprompt.com
In reply to: M Z (#19)
Re: remove contrib/xml2

M Z escribi�:

Hi Alvaro,

I followed your instruction but put the patch on 8.4.2 as I found it
crashes. It looks like the server still crash in the same way. Can you and
anyone give me some ideas how to fix this bug?

See src/backend/utils/adt/xml.c. Note the comment at the top:

/*
* Notes on memory management:
*
* Sometimes libxml allocates global structures in the hope that it can reuse
* them later on. This makes it impractical to change the xmlMemSetup
* functions on-the-fly; that is likely to lead to trying to pfree() chunks
* allocated with malloc() or vice versa. Since libxml might be used by
* loadable modules, eg libperl, our only safe choices are to change the
* functions at postmaster/backend launch or not at all. Since we'd rather
* not activate libxml in sessions that might never use it, the latter choice
* is the preferred one. However, for debugging purposes it can be awfully
* handy to constrain libxml's allocations to be done in a specific palloc
* context, where they're easy to track. Therefore there is code here that
* can be enabled in debug builds to redirect libxml's allocations into a
* special context LibxmlContext. It's not recommended to turn this on in
* a production build because of the possibility of bad interactions with
* external modules.
*/
/* #define USE_LIBXMLCONTEXT */

Then if you look at xpath.c in contrib/xml2 you notice that it's doing
exactly the thing that the core module says it's unreliable: using
palloc and friends in xmlMemSetup. So to fix the bug what's needed is
that the xmlMemSetup call in contrib is removed altogether, and all
memory is tracked and released by hand. It's rather tedious, and it's
also difficult to plug all resulting memory leaks. But AFAIUI doing
that would fix (some of?) the crashes. Not sure if your crash is in
this category.

--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#20)
Re: remove contrib/xml2

Alvaro Herrera <alvherre@commandprompt.com> writes:

Then if you look at xpath.c in contrib/xml2 you notice that it's doing
exactly the thing that the core module says it's unreliable: using
palloc and friends in xmlMemSetup. So to fix the bug what's needed is
that the xmlMemSetup call in contrib is removed altogether, and all
memory is tracked and released by hand. It's rather tedious, and it's
also difficult to plug all resulting memory leaks. But AFAIUI doing
that would fix (some of?) the crashes. Not sure if your crash is in
this category.

FWIW, the core xml code seems to have been pretty stable since we gave
up on trying to redirect libxml's memory allocations to palloc.
So what you basically need to do to xpath.c is something like this:
http://archives.postgresql.org/pgsql-committers/2009-05/msg00229.php

regards, tom lane

#22Dimitri Fontaine
dfontaine@hi-media.com
In reply to: Tom Lane (#21)
Re: remove contrib/xml2

Tom Lane <tgl@sss.pgh.pa.us> writes:

FWIW, the core xml code seems to have been pretty stable since we gave
up on trying to redirect libxml's memory allocations to palloc.
So what you basically need to do to xpath.c is something like this:
http://archives.postgresql.org/pgsql-committers/2009-05/msg00229.php

Which translates to this git URL, for lazy browsers such as me:

http://git.postgresql.org/gitweb?p=postgresql.git;a=commitdiff;h=f8059d7f8ae8bfac840fbda3c8efbc0f7c09b123
--
dim

#23Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#12)
Re: remove contrib/xml2

I believe I have fixed all the reported crashes in contrib/xml2.
However there is still this issue pointed out by Robert:

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<rowlist><row a="1"/><row a="2" b="oops"/></rowlist>');
SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row/@a|/rowlist/row/@b', 'true') as t(id int4, a text, b text);

which yields an answer that is, at least, extremely surprising, if not
flat-out wrong:

id | a | b
----+---+------
1 | 1 | oops
1 | 2 |
(2 rows)

the point being that it seems like "oops" should be associated with "2"
not "1". The reason for that behavior is that xpath_table runs through
the XPATH_NODESET results generated by the various XPaths and dumps the
k'th one of each into the k'th output row generated for the current
input row. If there is any way to synchronize which node in each array
goes with each node in each other array, it's not apparent to me, but
I don't know libxml's API at all. Perhaps there is some other call we
should be using to evaluate all the XPaths in parallel?

(The code is also unbelievably inefficient, recompiling each XPath
expression once per output row (!); but it doesn't seem worth fixing
that right away given that we might have to throw away the logic
entirely in order to fix this bug.)

regards, tom lane

#24Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#23)
Re: remove contrib/xml2

Tom Lane wrote:

I believe I have fixed all the reported crashes in contrib/xml2.

Yay! Well done! That at least removes any possibly urgency about
removing the module.

However there is still this issue pointed out by Robert:

CREATE TABLE xpath_test (id integer NOT NULL, t xml);
INSERT INTO xpath_test VALUES (1, '<rowlist><row a="1"/><row a="2" b="oops"/></rowlist>');
SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row/@a|/rowlist/row/@b', 'true') as t(id int4, a text, b text);

which yields an answer that is, at least, extremely surprising, if not
flat-out wrong:

id | a | b
----+---+------
1 | 1 | oops
1 | 2 |
(2 rows)

the point being that it seems like "oops" should be associated with "2"
not "1". The reason for that behavior is that xpath_table runs through
the XPATH_NODESET results generated by the various XPaths and dumps the
k'th one of each into the k'th output row generated for the current
input row. If there is any way to synchronize which node in each array
goes with each node in each other array, it's not apparent to me, but
I don't know libxml's API at all. Perhaps there is some other call we
should be using to evaluate all the XPaths in parallel?

(The code is also unbelievably inefficient, recompiling each XPath
expression once per output row (!); but it doesn't seem worth fixing
that right away given that we might have to throw away the logic
entirely in order to fix this bug.)

Damn that's ugly.

ISTM the missing piece is really in our API. We need to be able to
specify a nodeset to iterate over, and then for each node take the first
value produced by each xpath expression. So the example above would look
something like:

SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row', '@a|@b', 'true') as t(id int4, a text, b text);

Maybe we could approximate that with the current API by factoring out
the common root of the xpath expressions, but that's likely to be
extremely fragile and error prone, and we've already got bad experience
of trying to be too cute with xpath expressions.

cheers

andrew

#25Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#24)
Re: remove contrib/xml2

Andrew Dunstan <andrew@dunslane.net> writes:

Tom Lane wrote:

... The reason for that behavior is that xpath_table runs through
the XPATH_NODESET results generated by the various XPaths and dumps the
k'th one of each into the k'th output row generated for the current
input row.

Damn that's ugly.

Yup :-(

ISTM the missing piece is really in our API. We need to be able to
specify a nodeset to iterate over, and then for each node take the first
value produced by each xpath expression. So the example above would look
something like:

SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row', '@a|@b', 'true') as t(id int4, a text, b text);

Hm. It seems like that still leaves you open to the possibility of
out-of-sync results. If you consider the current behavior as what
you'd get with an empty root nodeset spec, then restricting it to
produce only the first output row doesn't help at all -- it would still
associate "1" with "oops". In general if the nodeset spec doesn't
select a unique subnode then you're at risk of bogus answers.
Maybe that could be defined as user error but it sure seems like it
would be error-prone to use.

Maybe we could approximate that with the current API by factoring out
the common root of the xpath expressions, but that's likely to be
extremely fragile and error prone, and we've already got bad experience
of trying to be too cute with xpath expressions.

Agreed, we do not want to be doing textual manipulations of XPaths,
which is what burnt us before. But does libxml2 offer any more
abstract path representation we could work on?

regards, tom lane

#26Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#25)
Re: remove contrib/xml2

Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

Tom Lane wrote:

... The reason for that behavior is that xpath_table runs through
the XPATH_NODESET results generated by the various XPaths and dumps the
k'th one of each into the k'th output row generated for the current
input row.

ISTM the missing piece is really in our API. We need to be able to
specify a nodeset to iterate over, and then for each node take the first
value produced by each xpath expression. So the example above would look
something like:

SELECT * FROM xpath_table('id', 't', 'xpath_test',
'/rowlist/row', '@a|@b', 'true') as t(id int4, a text, b text);

Hm. It seems like that still leaves you open to the possibility of
out-of-sync results. If you consider the current behavior as what
you'd get with an empty root nodeset spec, then restricting it to
produce only the first output row doesn't help at all -- it would still
associate "1" with "oops". In general if the nodeset spec doesn't
select a unique subnode then you're at risk of bogus answers.
Maybe that could be defined as user error but it sure seems like it
would be error-prone to use.

Well, I think that's going to be hard or impossible to avoid in the
general case. My suggestion was intended to give the user a much better
chance of avoiding it, however.

Arbitrary XML (or JSON or YAML or any artbitrarilly tree structured data
markup) doesn't map well to a rectangular structure, and this is always
likely to cause problems like this IMO.

I guess in the end the user could preprocess the XML with XSLT to remove
the irregularities before passing to to xpath_table.

We certainly need to put some warnings in the docs about it.

cheers

andrew