diff --git a/contrib/xml2/Makefile b/contrib/xml2/Makefile
index b96defa5d60..3426deaa25d 100644
--- a/contrib/xml2/Makefile
+++ b/contrib/xml2/Makefile
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/contrib/xml2/Makefile,v 1.12 2008/05/08 16:49:37 tgl Exp $
+# $PostgreSQL: pgsql/contrib/xml2/Makefile,v 1.13 2010/02/28 21:31:57 tgl Exp $
MODULE_big = pgxml
@@ -8,6 +8,7 @@ SHLIB_LINK += $(filter -lxslt, $(LIBS)) $(filter -lxml2, $(LIBS))
DATA_built = pgxml.sql
DATA = uninstall_pgxml.sql
+REGRESS = xml2
ifdef USE_PGXS
PG_CONFIG = pg_config
diff --git a/contrib/xml2/expected/xml2.out b/contrib/xml2/expected/xml2.out
new file mode 100644
index 00000000000..74896b08020
--- /dev/null
+++ b/contrib/xml2/expected/xml2.out
@@ -0,0 +1,147 @@
+--
+-- first, define the functions. Turn off echoing so that expected file
+-- does not depend on contents of pgxml.sql.
+--
+SET client_min_messages = warning;
+\set ECHO none
+RESET client_min_messages;
+select query_to_xml('select 1 as x',true,false,'');
+ query_to_xml
+---------------------------------------------------------------
+
+
+ +
+ +
+ 1 +
+
+
+ +
+
+
+
+(1 row)
+
+select xslt_process( query_to_xml('select x from generate_series(1,5) as
+x',true,false,'')::text,
+$$
+
+
+
+
+
+
+
+
+
+
+
+$$::text);
+ xslt_process
+---------------------------------------------------------------
+ +
+ +
+ +
+ +
+ 1 +
+
+
+ +
+ +
+ 2 +
+
+
+ +
+ +
+ 3 +
+
+
+ +
+ +
+ 4 +
+
+
+ +
+ +
+ 5 +
+
+
+ +
+
+
+
+(1 row)
+
+CREATE TABLE xpath_test (id integer NOT NULL, t xml);
+INSERT INTO xpath_test VALUES (1, '1');
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4);
+ id
+----
+(0 rows)
+
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4, doc int4);
+ id | doc
+----+-----
+ 1 | 1
+(1 row)
+
+DROP TABLE xpath_test;
+CREATE TABLE xpath_test (id integer NOT NULL, t text);
+INSERT INTO xpath_test VALUES (1, '1');
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4);
+ id
+----
+(0 rows)
+
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4, doc int4);
+ id | doc
+----+-----
+ 1 | 1
+(1 row)
+
+create table articles (article_id integer, article_xml xml, date_entered date);
+insert into articles (article_id, article_xml, date_entered)
+values (2, 'test37', now());
+SELECT * FROM
+xpath_table('article_id',
+ 'article_xml',
+ 'articles',
+ '/article/author|/article/pages|/article/title',
+ 'date_entered > ''2003-01-01'' ')
+AS t(article_id integer, author text, page_count integer, title text);
+ article_id | author | page_count | title
+------------+--------+------------+-------
+ 2 | test | 37 |
+(1 row)
+
+-- this used to fail when invoked a second time
+select xslt_process('',$$
+
+
+
+
+
+$$)::xml;
+ xslt_process
+--------------
+ +
+
+(1 row)
+
+select xslt_process('',$$
+
+
+
+
+
+$$)::xml;
+ xslt_process
+--------------
+ +
+
+(1 row)
+
+create table t1 (id integer, xml_data xml);
+insert into t1 (id, xml_data)
+values
+(1, 'Some
+Value');
+create index idx_xpath on t1 ( xpath_string
+('/attributes/attribute[@name="attr_1"]/text()', xml_data::text));
diff --git a/contrib/xml2/sql/xml2.sql b/contrib/xml2/sql/xml2.sql
new file mode 100644
index 00000000000..73723b6be10
--- /dev/null
+++ b/contrib/xml2/sql/xml2.sql
@@ -0,0 +1,82 @@
+--
+-- first, define the functions. Turn off echoing so that expected file
+-- does not depend on contents of pgxml.sql.
+--
+SET client_min_messages = warning;
+\set ECHO none
+\i pgxml.sql
+\set ECHO all
+RESET client_min_messages;
+
+select query_to_xml('select 1 as x',true,false,'');
+
+select xslt_process( query_to_xml('select x from generate_series(1,5) as
+x',true,false,'')::text,
+$$
+
+
+
+
+
+
+
+
+
+
+
+$$::text);
+
+CREATE TABLE xpath_test (id integer NOT NULL, t xml);
+INSERT INTO xpath_test VALUES (1, '1');
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4);
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4, doc int4);
+
+DROP TABLE xpath_test;
+CREATE TABLE xpath_test (id integer NOT NULL, t text);
+INSERT INTO xpath_test VALUES (1, '1');
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4);
+SELECT * FROM xpath_table('id', 't', 'xpath_test', '/doc/int', 'true')
+as t(id int4, doc int4);
+
+create table articles (article_id integer, article_xml xml, date_entered date);
+insert into articles (article_id, article_xml, date_entered)
+values (2, 'test37', now());
+SELECT * FROM
+xpath_table('article_id',
+ 'article_xml',
+ 'articles',
+ '/article/author|/article/pages|/article/title',
+ 'date_entered > ''2003-01-01'' ')
+AS t(article_id integer, author text, page_count integer, title text);
+
+-- this used to fail when invoked a second time
+select xslt_process('',$$
+
+
+
+
+
+$$)::xml;
+
+select xslt_process('',$$
+
+
+
+
+
+$$)::xml;
+
+create table t1 (id integer, xml_data xml);
+insert into t1 (id, xml_data)
+values
+(1, 'Some
+Value');
+
+create index idx_xpath on t1 ( xpath_string
+('/attributes/attribute[@name="attr_1"]/text()', xml_data::text));
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index b2458e9b114..8e289bdc499 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -1,5 +1,5 @@
/*
- * $PostgreSQL: pgsql/contrib/xml2/xpath.c,v 1.26 2010/02/28 19:51:37 tgl Exp $
+ * $PostgreSQL: pgsql/contrib/xml2/xpath.c,v 1.27 2010/02/28 21:31:57 tgl Exp $
*
* Parser interface for DOM-based parser (libxml) rather than
* stream-based SAX-type parser
@@ -42,10 +42,6 @@ void pgxml_parser_init(void);
/* local declarations */
-static void *pgxml_palloc(size_t size);
-static void *pgxml_repalloc(void *ptr, size_t size);
-static void pgxml_pfree(void *ptr);
-static char *pgxml_pstrdup(const char *string);
static void pgxml_errorHandler(void *ctxt, const char *msg,...);
static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
@@ -59,41 +55,10 @@ static xmlChar *pgxml_texttoxmlchar(text *textstring);
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath);
-
/* Global variables */
static char *pgxml_errorMsg = NULL; /* overall error message */
-/* memory handling passthrough functions (e.g. palloc, pstrdup are
- currently macros, and the others might become so...) */
-
-static void *
-pgxml_palloc(size_t size)
-{
-/* elog(DEBUG1,"Alloc %d in CMC %p",size,CurrentMemoryContext); */
- return palloc(size);
-}
-
-static void *
-pgxml_repalloc(void *ptr, size_t size)
-{
-/* elog(DEBUG1,"ReAlloc in CMC %p",CurrentMemoryContext);*/
- return repalloc(ptr, size);
-}
-
-static void
-pgxml_pfree(void *ptr)
-{
-/* elog(DEBUG1,"Free in CMC %p",CurrentMemoryContext); */
- pfree(ptr);
-}
-
-static char *
-pgxml_pstrdup(const char *string)
-{
- return pstrdup(string);
-}
-
/*
* The error handling function. This formats an error message and sets
* a flag - an ereport will be issued prior to return
@@ -157,9 +122,6 @@ pgxml_parser_init(void)
pgxml_errorMsg = NULL;
xmlSetGenericErrorFunc(NULL, pgxml_errorHandler);
- /* Replace libxml memory handling (DANGEROUS) */
- xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup);
-
/* Initialize libxml */
xmlInitParser();
@@ -627,7 +589,7 @@ xpath_table(PG_FUNCTION_ARGS)
int j;
int rownr; /* For issuing multiple rows from one original
* document */
- int had_values; /* To determine end of nodeset results */
+ bool had_values; /* To determine end of nodeset results */
StringInfoData query_buf;
/* We only have a valid tuple description in table function mode */
@@ -654,7 +616,6 @@ xpath_table(PG_FUNCTION_ARGS)
* 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);
@@ -671,6 +632,12 @@ xpath_table(PG_FUNCTION_ARGS)
/* get the requested return tuple description */
ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ /* must have at least one output column (for the pkey) */
+ if (ret_tupdesc->natts < 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("xpath_table must have at least one output column")));
+
/*
* 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
@@ -686,26 +653,27 @@ xpath_table(PG_FUNCTION_ARGS)
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 */
-
+ /*
+ * 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
+ while (numpaths < (ret_tupdesc->natts - 1))
{
- xpaths[numpaths] = (xmlChar *) pos;
+ xpaths[numpaths++] = (xmlChar *) pos;
pos = strstr(pos, pathsep);
if (pos != NULL)
{
*pos = '\0';
pos++;
}
- numpaths++;
- } while ((pos != NULL) && (numpaths < (ret_tupdesc->natts - 1)));
+ else
+ break;
+ }
/* Now build query */
initStringInfo(&query_buf);
@@ -800,7 +768,7 @@ xpath_table(PG_FUNCTION_ARGS)
do
{
/* Now evaluate the set of xpaths. */
- had_values = 0;
+ had_values = false;
for (j = 0; j < numpaths; j++)
{
ctxt = xmlXPathNewContext(doctree);
@@ -813,10 +781,7 @@ xpath_table(PG_FUNCTION_ARGS)
{
xmlCleanupParser();
xmlFreeDoc(doctree);
-
elog_error("XPath Syntax Error", true);
-
- PG_RETURN_NULL(); /* Keep compiler happy */
}
/* Now evaluate the path expression. */
@@ -829,11 +794,12 @@ xpath_table(PG_FUNCTION_ARGS)
{
case XPATH_NODESET:
/* We see if this nodeset has enough nodes */
- if ((res->nodesetval != NULL) && (rownr < res->nodesetval->nodeNr))
+ if (res->nodesetval != NULL &&
+ rownr < res->nodesetval->nodeNr)
{
resstr =
xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
- had_values = 1;
+ had_values = true;
}
else
resstr = NULL;