xml2 contrib patch supporting default XML namespaces

Started by Mike Rylanderalmost 19 years ago20 messages
#1Mike Rylander
mrylander@gmail.com
1 attachment(s)

Attatched you'll find a patch that I've been kicking around for a
while that I'd like to propose for inclusion in 8.3. I attempted to
submit this through the original xml2 author (as far back as the 7.4
days) but got no response.

It's really fairly trivial, but I will be using the features it
provides in production soon, so I'd like to see it applied against the
contrib xml2 module. The patch adds support for default XML
namespaces in xml2 by providing a mechanism for supplying a prefix to
a named namespace URI. It then wraps the namespace-capable functions
in backward-compatible equivalents so that old code will not break.

I have patched README.xml2, pgxml.sql.in and xpath.c against CVS HEAD
as of about 1 hour ago. I have code that uses both the old
non-namespace-capable functions and the new functions, and all
function as intended in my environment.

Please let me know if there is any more I can/need-to do to help this
patch along!

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

Attachments:

xml2-namespaces.patchapplication/octet-stream; name=xml2-namespaces.patchDownload
? contrib/xml2/.README.xml2.swp
Index: contrib/xml2/README.xml2
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/xml2/README.xml2,v
retrieving revision 1.7
diff -c -r1.7 README.xml2
*** contrib/xml2/README.xml2	16 Sep 2006 16:18:11 -0000	1.7
--- contrib/xml2/README.xml2	5 Mar 2007 22:13:30 -0000
***************
*** 44,54 ****
  xpath_number(document,query) RETURNS float4
  xpath_bool(document,query) RETURNS bool
  
  These functions evaluate the XPath query on the supplied document, and
! cast the result to the specified type.
  
  
  xpath_nodeset(document,query,toptag,itemtag) RETURNS text
  
  This evaluates query on document and wraps the result in XML tags. If
  the result is multivalued, the output will look like:
--- 44,61 ----
  xpath_number(document,query) RETURNS float4
  xpath_bool(document,query) RETURNS bool
  
+ xpath_string_ns(document,query,prefix,uri) RETURNS text
+ xpath_number_ns(document,query,prefix,uri) RETURNS float4
+ xpath_bool_ns(document,query,prefix,uri) RETURNS bool
+ 
  These functions evaluate the XPath query on the supplied document, and
! cast the result to the specified type.  The _ns variants supply support
! for documents with a default namespace by registering the given URI with
! the given prefix, simplifying the design of the XPath expression.
  
  
  xpath_nodeset(document,query,toptag,itemtag) RETURNS text
+ xpath_nodeset_ns(document,query,toptag,itemtag,prefix,uri) RETURNS text
  
  This evaluates query on document and wraps the result in XML tags. If
  the result is multivalued, the output will look like:
***************
*** 64,76 ****
--- 71,88 ----
  xpath_nodeset(document,query) RETURNS text omits both tags.
  xpath_nodeset(document,query,itemtag) RETURNS text omits toptag.
  
+ xpath_nodeset_ns(document,query,prefix,uri) RETURNS text omits both tags.
+ xpath_nodeset_ns(document,query,itemtag,prefix,uri) RETURNS text omits toptag.
+ 
  
  xpath_list(document,query,seperator) RETURNS text
+ xpath_list_ns(document,query,seperator,prefix,uri) RETURNS text
  
  This function returns multiple values seperated by the specified
  seperator, e.g. Value 1,Value 2,Value 3 if seperator=','.
  
  xpath_list(document,query) RETURNS text
+ xpath_list_ns(document,query,prefix,uri) RETURNS text
  
  This is a wrapper for the above function that uses ',' as the seperator.
  
***************
*** 87,92 ****
--- 99,105 ----
  The function itself takes 5 arguments, all text.
  
  xpath_table(key,document,relation,xpaths,criteria)
+ xpath_table_ns(key,document,relation,xpaths,criteria,prefix,uri)
  
  key - the name of the "key" field - this is just a field to be used as
  the first column of the output table i.e. it identifies the record from
Index: contrib/xml2/pgxml.sql.in
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/xml2/pgxml.sql.in,v
retrieving revision 1.9
diff -c -r1.9 pgxml.sql.in
*** contrib/xml2/pgxml.sql.in	16 Sep 2006 16:18:11 -0000	1.9
--- contrib/xml2/pgxml.sql.in	5 Mar 2007 22:13:30 -0000
***************
*** 10,54 ****
  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
--- 10,79 ----
  CREATE OR REPLACE FUNCTION xml_encode_special_chars(text) RETURNS text
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_string_ns(text,text,text,text) RETURNS text
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_string(text,text) RETURNS text 
! 	AS 'SELECT xpath_string_ns($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_nodeset_ns(text,text,text,text,text,text) RETURNS text
! 	AS 'MODULE_PATHNAME' LANGUAGE 'c' WITH (isStrict);
! 
! CREATE OR REPLACE FUNCTION xpath_number_ns(text,text,text,text) RETURNS float4
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_number(text,text) RETURNS float4 
! 	AS 'SELECT xpath_number_ns($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
! 
! CREATE OR REPLACE FUNCTION xpath_bool_ns(text,text,text,text) RETURNS boolean
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
+ CREATE OR REPLACE FUNCTION xpath_bool(text,text) RETURNS boolean 
+ 	AS 'SELECT xpath_bool_ns($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
+ 
  -- List function
  
! CREATE OR REPLACE FUNCTION xpath_list_ns(text,text,text,text,text) RETURNS text
! 	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
! 
  
+ CREATE OR REPLACE FUNCTION xpath_list(text,text,text) RETURNS text 
+ 	AS 'SELECT xpath_list_ns($1,$2,$3,'''','''')' LANGUAGE SQL 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,text,text) RETURNS text
! 	AS 'SELECT xpath_nodeset_ns($1,$2,$3,$4,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
  
+ 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;
+ 
+ 
+ CREATE OR REPLACE FUNCTION xpath_nodeset_ns(text,text,text,text) RETURNS text
+ 	AS 'SELECT xpath_nodeset_ns($1,$2,'''','''',$3,$4)' LANGUAGE SQL STRICT IMMUTABLE;
+ 
+ 
+ CREATE OR REPLACE FUNCTION xpath_nodeset_ns(text,text,text,text,text) RETURNS text
+ 	AS 'SELECT xpath_nodeset_ns($1,$2,'''',$3,$4,$5)' LANGUAGE SQL STRICT IMMUTABLE;
  
  
  -- Table function
  
+ CREATE OR REPLACE FUNCTION xpath_table_ns(text,text,text,text,text,text,text) RETURNS setof record
+ 	AS 'MODULE_PATHNAME' LANGUAGE C STRICT STABLE;
+ 
  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
Index: contrib/xml2/xpath.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/xml2/xpath.c,v
retrieving revision 1.15
diff -c -r1.15 xpath.c
*** contrib/xml2/xpath.c	27 Feb 2007 23:48:06 -0000	1.15
--- contrib/xml2/xpath.c	5 Mar 2007 22:13:30 -0000
***************
*** 15,20 ****
--- 15,21 ----
  #include <libxml/xmlmemory.h>
  #include <libxml/xmlerror.h>
  #include <libxml/parserInternals.h>
+ #include <libxml/xpathInternals.h>
  
  
  PG_MODULE_MAGIC;
***************
*** 39,54 ****
  
  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 */
--- 40,55 ----
  
  xmlChar    *pgxml_texttoxmlchar(text *textstring);
  
! static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar * xpath, const xmlChar * prefix,  const xmlChar * ns_uri);
  
  Datum		xml_is_well_formed(PG_FUNCTION_ARGS);
  Datum		xml_encode_special_chars(PG_FUNCTION_ARGS);
! Datum		xpath_nodeset_ns(PG_FUNCTION_ARGS);
! Datum		xpath_string_ns(PG_FUNCTION_ARGS);
! Datum		xpath_number_ns(PG_FUNCTION_ARGS);
! Datum		xpath_bool_ns(PG_FUNCTION_ARGS);
! Datum		xpath_list_ns(PG_FUNCTION_ARGS);
! Datum		xpath_table_ns(PG_FUNCTION_ARGS);
  Datum		xpath_table(PG_FUNCTION_ARGS);
  
  /* Global variables */
***************
*** 327,340 ****
   * properly
   */
  
! PG_FUNCTION_INFO_V1(xpath_nodeset);
  
  Datum
! xpath_nodeset(PG_FUNCTION_ARGS)
  {
  	xmlChar    *xpath,
  			   *toptag,
! 			   *septag;
  	int32		pathsize;
  	text
  			   *xpathsupp,
--- 328,343 ----
   * properly
   */
  
! PG_FUNCTION_INFO_V1(xpath_nodeset_ns);
  
  Datum
! xpath_nodeset_ns(PG_FUNCTION_ARGS)
  {
  	xmlChar    *xpath,
  			   *toptag,
! 			   *septag,
! 			   *prefix,
! 			   *ns_uri;
  	int32		pathsize;
  	text
  			   *xpathsupp,
***************
*** 343,357 ****
  	/* 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 */
--- 346,362 ----
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
  
! 	toptag = GET_STR(PG_GETARG_TEXT_P(2));
! 	septag = GET_STR(PG_GETARG_TEXT_P(3));
! 	prefix = GET_STR(PG_GETARG_TEXT_P(4));
! 	ns_uri = GET_STR(PG_GETARG_TEXT_P(5));
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
  	xpres = pgxml_result_to_text(
! 								 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, prefix, ns_uri),
  								 toptag, septag, NULL);
  
  	/* xmlCleanupParser(); done by result_to_text routine */
***************
*** 365,377 ****
  /*	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,
--- 370,385 ----
  /*	The following function is almost identical, but returns the elements in */
  /*	a list. */
  
! PG_FUNCTION_INFO_V1(xpath_list_ns);
  
  Datum
! xpath_list_ns(PG_FUNCTION_ARGS)
  {
  	xmlChar    *xpath,
+ 			   *ns_prefix,
+ 			   *ns_uri,
  			   *plainsep;
+ 
  	int32		pathsize;
  	text
  			   *xpathsupp,
***************
*** 379,384 ****
--- 387,394 ----
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(4));	/* Default namespace URI */
  
  	plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
  
***************
*** 386,394 ****
  
  	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);
--- 396,402 ----
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
! 	xpres = pgxml_result_to_text( pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri), NULL, NULL, plainsep);
  
  	/* xmlCleanupParser(); done by result_to_text routine */
  	pfree(xpath);
***************
*** 399,410 ****
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_string);
  
  Datum
! xpath_string(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath;
  	int32		pathsize;
  	text
  			   *xpathsupp,
--- 407,421 ----
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_string_ns);
  
  Datum
! xpath_string_ns(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath,
! 			   *ns_prefix,
! 			   *ns_uri;
! 
  	int32		pathsize;
  	text
  			   *xpathsupp,
***************
*** 412,417 ****
--- 423,430 ----
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(2));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace URI */
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
***************
*** 427,435 ****
  	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);
--- 440,446 ----
  	xpath[pathsize + 7] = ')';
  	xpath[pathsize + 8] = '\0';
  
! 	xpres = pgxml_result_to_text( pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri), NULL, NULL, NULL);
  
  	xmlCleanupParser();
  	pfree(xpath);
***************
*** 440,451 ****
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_number);
  
  Datum
! xpath_number(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath;
  	int32		pathsize;
  	text
  			   *xpathsupp;
--- 451,465 ----
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_number_ns);
  
  Datum
! xpath_number_ns(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath,
! 			   *ns_prefix,
! 			   *ns_uri;
! 
  	int32		pathsize;
  	text
  			   *xpathsupp;
***************
*** 456,467 ****
  
  	/* 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)
--- 470,483 ----
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(2));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace URI */
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
! 	res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri);
  	pfree(xpath);
  
  	if (res == NULL)
***************
*** 480,507 ****
  }
  
  
! 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)
--- 496,529 ----
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_bool_ns);
  
  Datum
! xpath_bool_ns(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath,
! 			   *ns_prefix,
! 			   *ns_uri;
! 
  	int32		pathsize;
  	text
  			   *xpathsupp;
  
+ 
  	int			bRes;
  
  	xmlXPathObjectPtr res;
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(2));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace URI */
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
! 	res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri);
  	pfree(xpath);
  
  	if (res == NULL)
***************
*** 521,527 ****
  /* Core function to evaluate XPath query */
  
  xmlXPathObjectPtr
! pgxml_xpath(text *document, xmlChar * xpath)
  {
  
  	xmlDocPtr	doctree;
--- 543,549 ----
  /* Core function to evaluate XPath query */
  
  xmlXPathObjectPtr
! pgxml_xpath(text *document, xmlChar * xpath, const xmlChar * prefix, const xmlChar * ns_uri)
  {
  
  	xmlDocPtr	doctree;
***************
*** 546,551 ****
--- 568,577 ----
  	ctxt = xmlXPathNewContext(doctree);
  	ctxt->node = xmlDocGetRootElement(doctree);
  
+ 	if (prefix != NULL)
+ 	{
+ 		xmlXPathRegisterNs( ctxt, prefix, ns_uri );
+ 	}
  
  	/* compile the path */
  	comppath = xmlXPathCompile(xpath);
***************
*** 626,635 ****
--- 652,985 ----
  	return xpres;
  }
  
+ 
  /* xpath_table is a table function. It needs some tidying (as do the
   * other functions here!
   */
  
+ PG_FUNCTION_INFO_V1(xpath_table_ns);
+ 
+ Datum
+ xpath_table_ns(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 = GET_STR(PG_GETARG_TEXT_P(0));
+ 	char	   *xmlfield = GET_STR(PG_GETARG_TEXT_P(1));
+ 	char	   *relname = GET_STR(PG_GETARG_TEXT_P(2));
+ 	char	   *xpathset = GET_STR(PG_GETARG_TEXT_P(3));
+ 	char	   *condition = GET_STR(PG_GETARG_TEXT_P(4));
+ 	char	   *ns_prefix = GET_STR(PG_GETARG_TEXT_P(5));
+ 	char	   *ns_uri = GET_STR(PG_GETARG_TEXT_P(6));
+ 
+ 	char	  **values;
+ 	xmlChar   **xpaths;
+ 	xmlChar    *pos;
+ 	xmlChar    *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_ns 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_ns 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(true, 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] = 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_ns: SPI_connect returned %d", ret);
+ 
+ 	if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
+ 		elog(ERROR, "xpath_table_ns: 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 */
+ 		doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
+ 
+ 		if (doctree == NULL)
+ 		{						/* not well-formed, so output all-NULL tuple */
+ 
+ 			ret_tuple = BuildTupleFromCStrings(attinmeta, values);
+ 			oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 			tuplestore_puttuple(tupstore, ret_tuple);
+ 			MemoryContextSwitchTo(oldcontext);
+ 			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);
+ 
+ 					if (ns_prefix != NULL)
+ 					{
+ 						xmlXPathRegisterNs( ctxt, ns_prefix, ns_uri );
+ 					}
+ 
+ 					/* 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("<unsupported/>");
+ 						}
+ 
+ 
+ 						/*
+ 						 * Insert this into the appropriate column in the
+ 						 * result tuple.
+ 						 */
+ 						values[j + 1] = resstr;
+ 					}
+ 					xmlXPathFreeContext(ctxt);
+ 				}
+ 				/* Now add the tuple to the output, if there is one. */
+ 				if (had_values)
+ 				{
+ 					ret_tuple = BuildTupleFromCStrings(attinmeta, values);
+ 					oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 					tuplestore_puttuple(tupstore, ret_tuple);
+ 					MemoryContextSwitchTo(oldcontext);
+ 					heap_freetuple(ret_tuple);
+ 				}
+ 
+ 				rownr++;
+ 
+ 			} while (had_values);
+ 
+ 		}
+ 
+ 		xmlFreeDoc(doctree);
+ 
+ 		pfree(pkey);
+ 		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;
+ 
+ }
+ 
  PG_FUNCTION_INFO_V1(xpath_table);
  
  Datum
#2Peter Eisentraut
peter_e@gmx.net
In reply to: Mike Rylander (#1)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Mike Rylander wrote:

The patch adds support for default XML namespaces in xml2 by providing
a mechanism for supplying a prefix to a named namespace URI.

How does it support multiple namespaces in one document?

--
Peter Eisentraut
http://developer.postgresql.org/~petere/

#3Nikolay Samokhvalov
samokhvalov@gmail.com
In reply to: Mike Rylander (#1)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

On 3/6/07, Mike Rylander <mrylander@gmail.com> wrote:

Attatched you'll find a patch that I've been kicking around for a
while that I'd like to propose for inclusion in 8.3. I attempted to
submit this through the original xml2 author (as far back as the 7.4
days) but got no response.

It's really fairly trivial, but I will be using the features it
provides in production soon, so I'd like to see it applied against the
contrib xml2 module. The patch adds support for default XML
namespaces in xml2 by providing a mechanism for supplying a prefix to
a named namespace URI. It then wraps the namespace-capable functions
in backward-compatible equivalents so that old code will not break.

1) And what about non-default namespaces?
2) What if my XPath query has different prefix, that also should be
mapped to the same URI? (Not frequent case, but this really can occur
-- e.g. XML doc has prefix 'local' for URI='http://127.0.0.1&#39;, but
XPath should have 'loc' for the same URI.)

--
Best regards,
Nikolay

#4Mike Rylander
mrylander@gmail.com
In reply to: Peter Eisentraut (#2)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

On 3/6/07, Peter Eisentraut <peter_e@gmx.net> wrote:

Mike Rylander wrote:

The patch adds support for default XML namespaces in xml2 by providing
a mechanism for supplying a prefix to a named namespace URI.

How does it support multiple namespaces in one document?

It supports one default (unprefixed) namespace URI per document, which
ISTM is the overwhelmingly common case (and the itch that I must
scratch).

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

#5Mike Rylander
mrylander@gmail.com
In reply to: Nikolay Samokhvalov (#3)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

On 3/6/07, Nikolay Samokhvalov <samokhvalov@gmail.com> wrote:

On 3/6/07, Mike Rylander <mrylander@gmail.com> wrote:

Attatched you'll find a patch that I've been kicking around for a
while that I'd like to propose for inclusion in 8.3. I attempted to
submit this through the original xml2 author (as far back as the 7.4
days) but got no response.

It's really fairly trivial, but I will be using the features it
provides in production soon, so I'd like to see it applied against the
contrib xml2 module. The patch adds support for default XML
namespaces in xml2 by providing a mechanism for supplying a prefix to
a named namespace URI. It then wraps the namespace-capable functions
in backward-compatible equivalents so that old code will not break.

1) And what about non-default namespaces?

I'm not sure I understand. If the namespace already has a prefix then
it works fine. This patch simply gives a known non-prefixed namespace
URI a prefix so one can write XPath that looks like

//marc:datafield[@tag='245']/marc:subfied[@code='a']

instead of

//*[local-name()='datafield' and
@tag='245']/*[local-name()='subfied' and @code='a']

A little two node example is painful enough, now imagine a non-trivial
example with backtracking conditionals... :P

2) What if my XPath query has different prefix, that also should be
mapped to the same URI? (Not frequent case, but this really can occur
-- e.g. XML doc has prefix 'local' for URI='http://127.0.0.1&#39;, but
XPath should have 'loc' for the same URI.)

Both prefixes work fine as multiple prefixes can map to the same URI.

--
Best regards,
Nikolay

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

#6Mike Rylander
mrylander@gmail.com
In reply to: Mike Rylander (#4)
1 attachment(s)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

On 3/6/07, Mike Rylander <mrylander@gmail.com> wrote:

On 3/6/07, Peter Eisentraut <peter_e@gmx.net> wrote:

Mike Rylander wrote:

The patch adds support for default XML namespaces in xml2 by providing
a mechanism for supplying a prefix to a named namespace URI.

How does it support multiple namespaces in one document?

It supports one default (unprefixed) namespace URI per document, which
ISTM is the overwhelmingly common case (and the itch that I must
scratch).

I think there is some confusion about what the current xml2 contrib
module supports and what my patch adds. The current code, as it
stands today, supports multiple namespaces just fine. The only
requirement is that each namespace have a prefix, or else one is
forced to use the local-name() construct with every single node for
those nodes in unprefixed ("default") namespaces. This patch simply
adds support for registering a prefix for an unprefixed namespace,
which is an extremely common case in XML and causes the use of overly
verbose contortions when designing XPath expressions. To illustrate
this, xml2 currently supports all of these statements:

SELECT xpath_nodeset('<x><y>foo</y></x>','/x/y');
SELECT xpath_nodeset('<x><a:y xmlns:a="uri:for:a">foo</a:y></x>','/x/a:y');
SELECT xpath_nodeset('<b:x xmlns:b="uri:for:b"><a:y
xmlns:a="uri:for:a">foo</a:y></b:x>','/b:x/a:y');

All number and manner of /prefixed/ namespaces work fine today.
However, in order to match an element or attribute with an unprefixed
namespace, the xpath becomes a study in overly verbose, human error
inducing repetition. For instance, consider the extremely common case
of an xhtml document that does not use a prefix for the xhtml
namespace. Using the xml2 contrib module as it stands today, without
my patch, using XPath to get the title of the document might look
something like this:

/*[local-name()="html"]/*[local-name()="head"]/*[local-name()="title"]

Now just imagine the XPath needed to get a portion of the body in a
nested div based on the existence of some other node ... the logic
gets lost in the noise simply because of the overhead of
namespace-qualifying the elements.

Namespaces were introduced in XML to address verbosity issues (among
other things), but as XPath was designed primarily as a language for
use inside XSLT (where namespace support is fully integrated) it
didn't get the treatment needed to handle unprefixed namespaces. To
address /that/ issue, my patch allows the registration of a supplied
prefix for a supplied URI, which solves the common "default namespace"
problem in a completely backward compatible way. The above example
XPath can now become:

/x:html/x:head/x:title

simply by supplying 2 more arguments to the _ns version of any of the
xpath_ functions available in xml2. I challenge anyone to claim that
the [local-name()="foo] variant is easier to read and less error prone
than the second, namespace-prefixed variant. They are exactly
equivalent, but the second (quite obviously) is Better(tm).

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

So, to sum up, please let me know what I can do to increase the
chances of getting this patch included. Alternatively, if my patch is
being vetoed, please let me know that too so that I can create a local
maintenance plan for this.

Thanks in advance. I've attached the patch again for reference.

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

Attachments:

xml2-namespaces.patchapplication/octet-stream; name=xml2-namespaces.patchDownload
? contrib/xml2/.README.xml2.swp
Index: contrib/xml2/README.xml2
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/xml2/README.xml2,v
retrieving revision 1.7
diff -c -r1.7 README.xml2
*** contrib/xml2/README.xml2	16 Sep 2006 16:18:11 -0000	1.7
--- contrib/xml2/README.xml2	5 Mar 2007 22:13:30 -0000
***************
*** 44,54 ****
  xpath_number(document,query) RETURNS float4
  xpath_bool(document,query) RETURNS bool
  
  These functions evaluate the XPath query on the supplied document, and
! cast the result to the specified type.
  
  
  xpath_nodeset(document,query,toptag,itemtag) RETURNS text
  
  This evaluates query on document and wraps the result in XML tags. If
  the result is multivalued, the output will look like:
--- 44,61 ----
  xpath_number(document,query) RETURNS float4
  xpath_bool(document,query) RETURNS bool
  
+ xpath_string_ns(document,query,prefix,uri) RETURNS text
+ xpath_number_ns(document,query,prefix,uri) RETURNS float4
+ xpath_bool_ns(document,query,prefix,uri) RETURNS bool
+ 
  These functions evaluate the XPath query on the supplied document, and
! cast the result to the specified type.  The _ns variants supply support
! for documents with a default namespace by registering the given URI with
! the given prefix, simplifying the design of the XPath expression.
  
  
  xpath_nodeset(document,query,toptag,itemtag) RETURNS text
+ xpath_nodeset_ns(document,query,toptag,itemtag,prefix,uri) RETURNS text
  
  This evaluates query on document and wraps the result in XML tags. If
  the result is multivalued, the output will look like:
***************
*** 64,76 ****
--- 71,88 ----
  xpath_nodeset(document,query) RETURNS text omits both tags.
  xpath_nodeset(document,query,itemtag) RETURNS text omits toptag.
  
+ xpath_nodeset_ns(document,query,prefix,uri) RETURNS text omits both tags.
+ xpath_nodeset_ns(document,query,itemtag,prefix,uri) RETURNS text omits toptag.
+ 
  
  xpath_list(document,query,seperator) RETURNS text
+ xpath_list_ns(document,query,seperator,prefix,uri) RETURNS text
  
  This function returns multiple values seperated by the specified
  seperator, e.g. Value 1,Value 2,Value 3 if seperator=','.
  
  xpath_list(document,query) RETURNS text
+ xpath_list_ns(document,query,prefix,uri) RETURNS text
  
  This is a wrapper for the above function that uses ',' as the seperator.
  
***************
*** 87,92 ****
--- 99,105 ----
  The function itself takes 5 arguments, all text.
  
  xpath_table(key,document,relation,xpaths,criteria)
+ xpath_table_ns(key,document,relation,xpaths,criteria,prefix,uri)
  
  key - the name of the "key" field - this is just a field to be used as
  the first column of the output table i.e. it identifies the record from
Index: contrib/xml2/pgxml.sql.in
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/xml2/pgxml.sql.in,v
retrieving revision 1.9
diff -c -r1.9 pgxml.sql.in
*** contrib/xml2/pgxml.sql.in	16 Sep 2006 16:18:11 -0000	1.9
--- contrib/xml2/pgxml.sql.in	5 Mar 2007 22:13:30 -0000
***************
*** 10,54 ****
  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
--- 10,79 ----
  CREATE OR REPLACE FUNCTION xml_encode_special_chars(text) RETURNS text
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_string_ns(text,text,text,text) RETURNS text
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_string(text,text) RETURNS text 
! 	AS 'SELECT xpath_string_ns($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_nodeset_ns(text,text,text,text,text,text) RETURNS text
! 	AS 'MODULE_PATHNAME' LANGUAGE 'c' WITH (isStrict);
! 
! CREATE OR REPLACE FUNCTION xpath_number_ns(text,text,text,text) RETURNS float4
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
! CREATE OR REPLACE FUNCTION xpath_number(text,text) RETURNS float4 
! 	AS 'SELECT xpath_number_ns($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
! 
! CREATE OR REPLACE FUNCTION xpath_bool_ns(text,text,text,text) RETURNS boolean
  	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
  
+ CREATE OR REPLACE FUNCTION xpath_bool(text,text) RETURNS boolean 
+ 	AS 'SELECT xpath_bool_ns($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
+ 
  -- List function
  
! CREATE OR REPLACE FUNCTION xpath_list_ns(text,text,text,text,text) RETURNS text
! 	AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE;
! 
  
+ CREATE OR REPLACE FUNCTION xpath_list(text,text,text) RETURNS text 
+ 	AS 'SELECT xpath_list_ns($1,$2,$3,'''','''')' LANGUAGE SQL 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,text,text) RETURNS text
! 	AS 'SELECT xpath_nodeset_ns($1,$2,$3,$4,'''','''')' LANGUAGE SQL STRICT IMMUTABLE;
  
+ 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;
+ 
+ 
+ CREATE OR REPLACE FUNCTION xpath_nodeset_ns(text,text,text,text) RETURNS text
+ 	AS 'SELECT xpath_nodeset_ns($1,$2,'''','''',$3,$4)' LANGUAGE SQL STRICT IMMUTABLE;
+ 
+ 
+ CREATE OR REPLACE FUNCTION xpath_nodeset_ns(text,text,text,text,text) RETURNS text
+ 	AS 'SELECT xpath_nodeset_ns($1,$2,'''',$3,$4,$5)' LANGUAGE SQL STRICT IMMUTABLE;
  
  
  -- Table function
  
+ CREATE OR REPLACE FUNCTION xpath_table_ns(text,text,text,text,text,text,text) RETURNS setof record
+ 	AS 'MODULE_PATHNAME' LANGUAGE C STRICT STABLE;
+ 
  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
Index: contrib/xml2/xpath.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/xml2/xpath.c,v
retrieving revision 1.15
diff -c -r1.15 xpath.c
*** contrib/xml2/xpath.c	27 Feb 2007 23:48:06 -0000	1.15
--- contrib/xml2/xpath.c	5 Mar 2007 22:13:30 -0000
***************
*** 15,20 ****
--- 15,21 ----
  #include <libxml/xmlmemory.h>
  #include <libxml/xmlerror.h>
  #include <libxml/parserInternals.h>
+ #include <libxml/xpathInternals.h>
  
  
  PG_MODULE_MAGIC;
***************
*** 39,54 ****
  
  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 */
--- 40,55 ----
  
  xmlChar    *pgxml_texttoxmlchar(text *textstring);
  
! static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar * xpath, const xmlChar * prefix,  const xmlChar * ns_uri);
  
  Datum		xml_is_well_formed(PG_FUNCTION_ARGS);
  Datum		xml_encode_special_chars(PG_FUNCTION_ARGS);
! Datum		xpath_nodeset_ns(PG_FUNCTION_ARGS);
! Datum		xpath_string_ns(PG_FUNCTION_ARGS);
! Datum		xpath_number_ns(PG_FUNCTION_ARGS);
! Datum		xpath_bool_ns(PG_FUNCTION_ARGS);
! Datum		xpath_list_ns(PG_FUNCTION_ARGS);
! Datum		xpath_table_ns(PG_FUNCTION_ARGS);
  Datum		xpath_table(PG_FUNCTION_ARGS);
  
  /* Global variables */
***************
*** 327,340 ****
   * properly
   */
  
! PG_FUNCTION_INFO_V1(xpath_nodeset);
  
  Datum
! xpath_nodeset(PG_FUNCTION_ARGS)
  {
  	xmlChar    *xpath,
  			   *toptag,
! 			   *septag;
  	int32		pathsize;
  	text
  			   *xpathsupp,
--- 328,343 ----
   * properly
   */
  
! PG_FUNCTION_INFO_V1(xpath_nodeset_ns);
  
  Datum
! xpath_nodeset_ns(PG_FUNCTION_ARGS)
  {
  	xmlChar    *xpath,
  			   *toptag,
! 			   *septag,
! 			   *prefix,
! 			   *ns_uri;
  	int32		pathsize;
  	text
  			   *xpathsupp,
***************
*** 343,357 ****
  	/* 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 */
--- 346,362 ----
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
  
! 	toptag = GET_STR(PG_GETARG_TEXT_P(2));
! 	septag = GET_STR(PG_GETARG_TEXT_P(3));
! 	prefix = GET_STR(PG_GETARG_TEXT_P(4));
! 	ns_uri = GET_STR(PG_GETARG_TEXT_P(5));
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
  	xpres = pgxml_result_to_text(
! 								 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, prefix, ns_uri),
  								 toptag, septag, NULL);
  
  	/* xmlCleanupParser(); done by result_to_text routine */
***************
*** 365,377 ****
  /*	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,
--- 370,385 ----
  /*	The following function is almost identical, but returns the elements in */
  /*	a list. */
  
! PG_FUNCTION_INFO_V1(xpath_list_ns);
  
  Datum
! xpath_list_ns(PG_FUNCTION_ARGS)
  {
  	xmlChar    *xpath,
+ 			   *ns_prefix,
+ 			   *ns_uri,
  			   *plainsep;
+ 
  	int32		pathsize;
  	text
  			   *xpathsupp,
***************
*** 379,384 ****
--- 387,394 ----
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(4));	/* Default namespace URI */
  
  	plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
  
***************
*** 386,394 ****
  
  	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);
--- 396,402 ----
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
! 	xpres = pgxml_result_to_text( pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri), NULL, NULL, plainsep);
  
  	/* xmlCleanupParser(); done by result_to_text routine */
  	pfree(xpath);
***************
*** 399,410 ****
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_string);
  
  Datum
! xpath_string(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath;
  	int32		pathsize;
  	text
  			   *xpathsupp,
--- 407,421 ----
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_string_ns);
  
  Datum
! xpath_string_ns(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath,
! 			   *ns_prefix,
! 			   *ns_uri;
! 
  	int32		pathsize;
  	text
  			   *xpathsupp,
***************
*** 412,417 ****
--- 423,430 ----
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(2));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace URI */
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
***************
*** 427,435 ****
  	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);
--- 440,446 ----
  	xpath[pathsize + 7] = ')';
  	xpath[pathsize + 8] = '\0';
  
! 	xpres = pgxml_result_to_text( pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri), NULL, NULL, NULL);
  
  	xmlCleanupParser();
  	pfree(xpath);
***************
*** 440,451 ****
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_number);
  
  Datum
! xpath_number(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath;
  	int32		pathsize;
  	text
  			   *xpathsupp;
--- 451,465 ----
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_number_ns);
  
  Datum
! xpath_number_ns(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath,
! 			   *ns_prefix,
! 			   *ns_uri;
! 
  	int32		pathsize;
  	text
  			   *xpathsupp;
***************
*** 456,467 ****
  
  	/* 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)
--- 470,483 ----
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(2));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace URI */
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
! 	res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri);
  	pfree(xpath);
  
  	if (res == NULL)
***************
*** 480,507 ****
  }
  
  
! 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)
--- 496,529 ----
  }
  
  
! PG_FUNCTION_INFO_V1(xpath_bool_ns);
  
  Datum
! xpath_bool_ns(PG_FUNCTION_ARGS)
  {
! 	xmlChar    *xpath,
! 			   *ns_prefix,
! 			   *ns_uri;
! 
  	int32		pathsize;
  	text
  			   *xpathsupp;
  
+ 
  	int			bRes;
  
  	xmlXPathObjectPtr res;
  
  	/* PG_GETARG_TEXT_P(0) is document buffer */
  	xpathsupp = PG_GETARG_TEXT_P(1);	/* XPath expression */
+ 	ns_prefix = GET_STR(PG_GETARG_TEXT_P(2));	/* Default namespace prefix */
+ 	ns_uri    = GET_STR(PG_GETARG_TEXT_P(3));	/* Default namespace URI */
  
  	pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
  
  	xpath = pgxml_texttoxmlchar(xpathsupp);
  
! 	res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath, ns_prefix, ns_uri);
  	pfree(xpath);
  
  	if (res == NULL)
***************
*** 521,527 ****
  /* Core function to evaluate XPath query */
  
  xmlXPathObjectPtr
! pgxml_xpath(text *document, xmlChar * xpath)
  {
  
  	xmlDocPtr	doctree;
--- 543,549 ----
  /* Core function to evaluate XPath query */
  
  xmlXPathObjectPtr
! pgxml_xpath(text *document, xmlChar * xpath, const xmlChar * prefix, const xmlChar * ns_uri)
  {
  
  	xmlDocPtr	doctree;
***************
*** 546,551 ****
--- 568,577 ----
  	ctxt = xmlXPathNewContext(doctree);
  	ctxt->node = xmlDocGetRootElement(doctree);
  
+ 	if (prefix != NULL)
+ 	{
+ 		xmlXPathRegisterNs( ctxt, prefix, ns_uri );
+ 	}
  
  	/* compile the path */
  	comppath = xmlXPathCompile(xpath);
***************
*** 626,635 ****
--- 652,985 ----
  	return xpres;
  }
  
+ 
  /* xpath_table is a table function. It needs some tidying (as do the
   * other functions here!
   */
  
+ PG_FUNCTION_INFO_V1(xpath_table_ns);
+ 
+ Datum
+ xpath_table_ns(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 = GET_STR(PG_GETARG_TEXT_P(0));
+ 	char	   *xmlfield = GET_STR(PG_GETARG_TEXT_P(1));
+ 	char	   *relname = GET_STR(PG_GETARG_TEXT_P(2));
+ 	char	   *xpathset = GET_STR(PG_GETARG_TEXT_P(3));
+ 	char	   *condition = GET_STR(PG_GETARG_TEXT_P(4));
+ 	char	   *ns_prefix = GET_STR(PG_GETARG_TEXT_P(5));
+ 	char	   *ns_uri = GET_STR(PG_GETARG_TEXT_P(6));
+ 
+ 	char	  **values;
+ 	xmlChar   **xpaths;
+ 	xmlChar    *pos;
+ 	xmlChar    *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_ns 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_ns 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(true, 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] = 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_ns: SPI_connect returned %d", ret);
+ 
+ 	if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
+ 		elog(ERROR, "xpath_table_ns: 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 */
+ 		doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
+ 
+ 		if (doctree == NULL)
+ 		{						/* not well-formed, so output all-NULL tuple */
+ 
+ 			ret_tuple = BuildTupleFromCStrings(attinmeta, values);
+ 			oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 			tuplestore_puttuple(tupstore, ret_tuple);
+ 			MemoryContextSwitchTo(oldcontext);
+ 			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);
+ 
+ 					if (ns_prefix != NULL)
+ 					{
+ 						xmlXPathRegisterNs( ctxt, ns_prefix, ns_uri );
+ 					}
+ 
+ 					/* 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("<unsupported/>");
+ 						}
+ 
+ 
+ 						/*
+ 						 * Insert this into the appropriate column in the
+ 						 * result tuple.
+ 						 */
+ 						values[j + 1] = resstr;
+ 					}
+ 					xmlXPathFreeContext(ctxt);
+ 				}
+ 				/* Now add the tuple to the output, if there is one. */
+ 				if (had_values)
+ 				{
+ 					ret_tuple = BuildTupleFromCStrings(attinmeta, values);
+ 					oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 					tuplestore_puttuple(tupstore, ret_tuple);
+ 					MemoryContextSwitchTo(oldcontext);
+ 					heap_freetuple(ret_tuple);
+ 				}
+ 
+ 				rownr++;
+ 
+ 			} while (had_values);
+ 
+ 		}
+ 
+ 		xmlFreeDoc(doctree);
+ 
+ 		pfree(pkey);
+ 		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;
+ 
+ }
+ 
  PG_FUNCTION_INFO_V1(xpath_table);
  
  Datum
#7Bruce Momjian
bruce@momjian.us
In reply to: Mike Rylander (#6)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Your patch has been added to the PostgreSQL unapplied patches list at:

http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

---------------------------------------------------------------------------

Mike Rylander wrote:

On 3/6/07, Mike Rylander <mrylander@gmail.com> wrote:

On 3/6/07, Peter Eisentraut <peter_e@gmx.net> wrote:

Mike Rylander wrote:

The patch adds support for default XML namespaces in xml2 by providing
a mechanism for supplying a prefix to a named namespace URI.

How does it support multiple namespaces in one document?

It supports one default (unprefixed) namespace URI per document, which
ISTM is the overwhelmingly common case (and the itch that I must
scratch).

I think there is some confusion about what the current xml2 contrib
module supports and what my patch adds. The current code, as it
stands today, supports multiple namespaces just fine. The only
requirement is that each namespace have a prefix, or else one is
forced to use the local-name() construct with every single node for
those nodes in unprefixed ("default") namespaces. This patch simply
adds support for registering a prefix for an unprefixed namespace,
which is an extremely common case in XML and causes the use of overly
verbose contortions when designing XPath expressions. To illustrate
this, xml2 currently supports all of these statements:

SELECT xpath_nodeset('<x><y>foo</y></x>','/x/y');
SELECT xpath_nodeset('<x><a:y xmlns:a="uri:for:a">foo</a:y></x>','/x/a:y');
SELECT xpath_nodeset('<b:x xmlns:b="uri:for:b"><a:y
xmlns:a="uri:for:a">foo</a:y></b:x>','/b:x/a:y');

All number and manner of /prefixed/ namespaces work fine today.
However, in order to match an element or attribute with an unprefixed
namespace, the xpath becomes a study in overly verbose, human error
inducing repetition. For instance, consider the extremely common case
of an xhtml document that does not use a prefix for the xhtml
namespace. Using the xml2 contrib module as it stands today, without
my patch, using XPath to get the title of the document might look
something like this:

/*[local-name()="html"]/*[local-name()="head"]/*[local-name()="title"]

Now just imagine the XPath needed to get a portion of the body in a
nested div based on the existence of some other node ... the logic
gets lost in the noise simply because of the overhead of
namespace-qualifying the elements.

Namespaces were introduced in XML to address verbosity issues (among
other things), but as XPath was designed primarily as a language for
use inside XSLT (where namespace support is fully integrated) it
didn't get the treatment needed to handle unprefixed namespaces. To
address /that/ issue, my patch allows the registration of a supplied
prefix for a supplied URI, which solves the common "default namespace"
problem in a completely backward compatible way. The above example
XPath can now become:

/x:html/x:head/x:title

simply by supplying 2 more arguments to the _ns version of any of the
xpath_ functions available in xml2. I challenge anyone to claim that
the [local-name()="foo] variant is easier to read and less error prone
than the second, namespace-prefixed variant. They are exactly
equivalent, but the second (quite obviously) is Better(tm).

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

So, to sum up, please let me know what I can do to increase the
chances of getting this patch included. Alternatively, if my patch is
being vetoed, please let me know that too so that I can create a local
maintenance plan for this.

Thanks in advance. I've attached the patch again for reference.

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

http://archives.postgresql.org

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#8Peter Eisentraut
peter_e@gmx.net
In reply to: Bruce Momjian (#7)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Bruce Momjian wrote:

Your patch has been added to the PostgreSQL unapplied patches list
at:

http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers
reviews and approves it.

I was hoping that we're deprecating contrib/xml2, so I wouldn't add more
features to it.

--
Peter Eisentraut
http://developer.postgresql.org/~petere/

#9Bruce Momjian
bruce@momjian.us
In reply to: Peter Eisentraut (#8)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Peter Eisentraut wrote:

Bruce Momjian wrote:

Your patch has been added to the PostgreSQL unapplied patches list
at:

http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers
reviews and approves it.

I was hoping that we're deprecating contrib/xml2, so I wouldn't add more
features to it.

Author states:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#10Dave Page
dpage@postgresql.org
In reply to: Bruce Momjian (#9)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Bruce Momjian wrote:

Peter Eisentraut wrote:

I was hoping that we're deprecating contrib/xml2, so I wouldn't add more
features to it.

Author states:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

But we don't add new features to stable branches, even if they're
backward compatible.

Regards, Dave

#11Bruce Momjian
bruce@momjian.us
In reply to: Dave Page (#10)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Dave Page wrote:

Bruce Momjian wrote:

Peter Eisentraut wrote:

I was hoping that we're deprecating contrib/xml2, so I wouldn't add more
features to it.

Author states:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

But we don't add new features to stable branches, even if they're
backward compatible.

I was quoting the text only to state the author realizes /contrib/xml2
is depricated in 8.3. This is not going into 8.2.X, only 8.3.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#9)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Bruce Momjian <bruce@momjian.us> writes:

Peter Eisentraut wrote:

I was hoping that we're deprecating contrib/xml2, so I wouldn't add more
features to it.

Author states:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

Well, it's not going to be put in future 8.1 or 8.2 releases, so the
above argument is not a reason to include it now. What the author
should do if he wants to offer a new feature for past release branches
is to put up a project on pgfoundry.

regards, tom lane

#13Mike Rylander
mrylander@gmail.com
In reply to: Tom Lane (#12)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

On 3/22/07, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Bruce Momjian <bruce@momjian.us> writes:

Peter Eisentraut wrote:

I was hoping that we're deprecating contrib/xml2, so I wouldn't add more
features to it.

Author states:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

Well, it's not going to be put in future 8.1 or 8.2 releases, so the
above argument is not a reason to include it now. What the author
should do if he wants to offer a new feature for past release branches
is to put up a project on pgfoundry.

regards, tom lane

Hmm.. OK. Well, thank you all for clarifying that. I thought
(perhaps only hoped?) that the bar was lower for contrib than for core
as far as features go, but it seems that assumption is incorrect.
I'll look at starting a pgfoundry project soon.

A related question, however: Will the XML features being included in
8.3 support namespace prefix registration? If not, handling arbitrary
XML via XPath that includes unprefixed (default) namespaces (for me
that is the majority of the XML I deal with, and no, I can't change
that) will have exactly the same problems using the new mechanisms as
with the current xml2 contrib module. I ask because, based on the
design emails I've seen on -hackers, nothing surrounding explicit
support for said issue jumped out at me.

Thanks again.

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

#14Andrew Dunstan
andrew@dunslane.net
In reply to: Mike Rylander (#13)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Mike Rylander wrote:

A related question, however: Will the XML features being included in
8.3 support namespace prefix registration? If not, handling arbitrary
XML via XPath that includes unprefixed (default) namespaces (for me
that is the majority of the XML I deal with, and no, I can't change
that) will have exactly the same problems using the new mechanisms as
with the current xml2 contrib module. I ask because, based on the
design emails I've seen on -hackers, nothing surrounding explicit
support for said issue jumped out at me.

If it won't you have 10 days to get in a patch.

cheers

andrew

#15Peter Eisentraut
peter_e@gmx.net
In reply to: Mike Rylander (#13)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Mike Rylander wrote:

A related question, however: �Will the XML features being included in
8.3 support namespace prefix registration?

That is certainly the plan.

--
Peter Eisentraut
http://developer.postgresql.org/~petere/

#16Joshua D. Drake
jd@commandprompt.com
In reply to: Peter Eisentraut (#15)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Peter Eisentraut wrote:

Mike Rylander wrote:

A related question, however: Will the XML features being included in
8.3 support namespace prefix registration?

That is certainly the plan.

Let me bounce my ostrich (sp?) head up here and say, thanks for your
work on this Peter.

Joshua D. Drake

--

=== The PostgreSQL Company: Command Prompt, Inc. ===
Sales/Support: +1.503.667.4564 || 24x7/Emergency: +1.800.492.2240
Providing the most comprehensive PostgreSQL solutions since 1997
http://www.commandprompt.com/

Donate to the PostgreSQL Project: http://www.postgresql.org/about/donate
PostgreSQL Replication: http://www.commandprompt.com/products/

#17Peter Eisentraut
peter_e@gmx.net
In reply to: Mike Rylander (#6)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Mike Rylander wrote:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable
(or, in fact, unwilling) to upgrade to 8.3 for quite some time.
�Because this patch is completely backward compatible it can
(theoretically) be included in future 8.1 and 8.2 releases, and for
those of us that need more full XML support in the short term the
upgrade of a contrib module is probably a very viable option -- it is
for me, anyway.

8.3 contains XPath support which should cover the issue that this patch
addresses. (Might wanna check.) Since we're not going to put new
features into earlier releases, and contrib modules are not necessarily
source backward-compatible, I don't think this patch has a place in a
future PostgreSQL release.

--
Peter Eisentraut
http://developer.postgresql.org/~petere/

#18Bruce Momjian
bruce@momjian.us
In reply to: Peter Eisentraut (#17)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

Peter Eisentraut wrote:

Mike Rylander wrote:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable
(or, in fact, unwilling) to upgrade to 8.3 for quite some time.
?Because this patch is completely backward compatible it can
(theoretically) be included in future 8.1 and 8.2 releases, and for
those of us that need more full XML support in the short term the
upgrade of a contrib module is probably a very viable option -- it is
for me, anyway.

8.3 contains XPath support which should cover the issue that this patch
addresses. (Might wanna check.) Since we're not going to put new
features into earlier releases, and contrib modules are not necessarily
source backward-compatible, I don't think this patch has a place in a
future PostgreSQL release.

Agreed. XML is now more stabilized in the backend than when the patch
appeared, and it doesn't make sense to add features to an interface that
is to be used only for backward compatability.

Patch rejected.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#19Mike Rylander
mrylander@gmail.com
In reply to: Peter Eisentraut (#17)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

On 6/1/07, Peter Eisentraut <peter_e@gmx.net> wrote:

Mike Rylander wrote:

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable
(or, in fact, unwilling) to upgrade to 8.3 for quite some time.
Because this patch is completely backward compatible it can
(theoretically) be included in future 8.1 and 8.2 releases, and for
those of us that need more full XML support in the short term the
upgrade of a contrib module is probably a very viable option -- it is
for me, anyway.

8.3 contains XPath support which should cover the issue that this patch
addresses. (Might wanna check.) Since we're not going to put new
features into earlier releases, and contrib modules are not necessarily
source backward-compatible, I don't think this patch has a place in a
future PostgreSQL release.

I agree, assuming the upcoming XML support can handle default
(unprefixed) namespaces.

--
Mike Rylander

#20Bruce Momjian
bruce@momjian.us
In reply to: Mike Rylander (#6)
Re: [PATCHES] xml2 contrib patch supporting default XML namespaces

XML is now more stabilized in the backend than when the patch appeared,
and it doesn't make sense to add features to a /contrib interface that
is to be used only for backward compatability.

Patch rejected. You can put the patch on pgfoundry if you are worried
others might need this functionality.

---------------------------------------------------------------------------

Mike Rylander wrote:

On 3/6/07, Mike Rylander <mrylander@gmail.com> wrote:

On 3/6/07, Peter Eisentraut <peter_e@gmx.net> wrote:

Mike Rylander wrote:

The patch adds support for default XML namespaces in xml2 by providing
a mechanism for supplying a prefix to a named namespace URI.

How does it support multiple namespaces in one document?

It supports one default (unprefixed) namespace URI per document, which
ISTM is the overwhelmingly common case (and the itch that I must
scratch).

I think there is some confusion about what the current xml2 contrib
module supports and what my patch adds. The current code, as it
stands today, supports multiple namespaces just fine. The only
requirement is that each namespace have a prefix, or else one is
forced to use the local-name() construct with every single node for
those nodes in unprefixed ("default") namespaces. This patch simply
adds support for registering a prefix for an unprefixed namespace,
which is an extremely common case in XML and causes the use of overly
verbose contortions when designing XPath expressions. To illustrate
this, xml2 currently supports all of these statements:

SELECT xpath_nodeset('<x><y>foo</y></x>','/x/y');
SELECT xpath_nodeset('<x><a:y xmlns:a="uri:for:a">foo</a:y></x>','/x/a:y');
SELECT xpath_nodeset('<b:x xmlns:b="uri:for:b"><a:y
xmlns:a="uri:for:a">foo</a:y></b:x>','/b:x/a:y');

All number and manner of /prefixed/ namespaces work fine today.
However, in order to match an element or attribute with an unprefixed
namespace, the xpath becomes a study in overly verbose, human error
inducing repetition. For instance, consider the extremely common case
of an xhtml document that does not use a prefix for the xhtml
namespace. Using the xml2 contrib module as it stands today, without
my patch, using XPath to get the title of the document might look
something like this:

/*[local-name()="html"]/*[local-name()="head"]/*[local-name()="title"]

Now just imagine the XPath needed to get a portion of the body in a
nested div based on the existence of some other node ... the logic
gets lost in the noise simply because of the overhead of
namespace-qualifying the elements.

Namespaces were introduced in XML to address verbosity issues (among
other things), but as XPath was designed primarily as a language for
use inside XSLT (where namespace support is fully integrated) it
didn't get the treatment needed to handle unprefixed namespaces. To
address /that/ issue, my patch allows the registration of a supplied
prefix for a supplied URI, which solves the common "default namespace"
problem in a completely backward compatible way. The above example
XPath can now become:

/x:html/x:head/x:title

simply by supplying 2 more arguments to the _ns version of any of the
xpath_ functions available in xml2. I challenge anyone to claim that
the [local-name()="foo] variant is easier to read and less error prone
than the second, namespace-prefixed variant. They are exactly
equivalent, but the second (quite obviously) is Better(tm).

I understand that XML support is planned and at least partially
implemented for 8.3, but many production instances will be unable (or,
in fact, unwilling) to upgrade to 8.3 for quite some time. Because
this patch is completely backward compatible it can (theoretically) be
included in future 8.1 and 8.2 releases, and for those of us that need
more full XML support in the short term the upgrade of a contrib
module is probably a very viable option -- it is for me, anyway.

So, to sum up, please let me know what I can do to increase the
chances of getting this patch included. Alternatively, if my patch is
being vetoed, please let me know that too so that I can create a local
maintenance plan for this.

Thanks in advance. I've attached the patch again for reference.

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

http://archives.postgresql.org

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +