? 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
