From: Tom Lane Date: Mon, 1 Mar 2010 03:41:04 +0000 (+0000) Subject: Back-patch today's memory management fixups in contrib/xml2. X-Git-Tag: REL8_4_3~28 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8748dc35800d7a4f46cf469ede12583b5fb52a9e;p=postgresql Back-patch today's memory management fixups in contrib/xml2. Prior to 8.3, these changes are not critical for compatibility with core Postgres, since core had no libxml2 calls then. However there is still a risk if contrib/xml2 is used along with libxml2 functionality in Perl or other loadable modules. So back-patch to all versions. Also back-patch addition of regression tests. I'm not sure how many of the cases are interesting without the interaction with core xml code, but a silly regression test is still better than none at all. --- diff --git a/contrib/xml2/Makefile b/contrib/xml2/Makefile index b96defa5d6..314d9964fd 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.12.2.1 2010/03/01 03:41:04 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 0000000000..7dd05fad9a --- /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 0000000000..73723b6be1 --- /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 99be6f91d2..500a4e37e5 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -1,5 +1,5 @@ /* - * $PostgreSQL: pgsql/contrib/xml2/xpath.c,v 1.23.2.1 2009/12/29 17:41:09 heikki Exp $ + * $PostgreSQL: pgsql/contrib/xml2/xpath.c,v 1.23.2.2 2010/03/01 03:41:04 tgl Exp $ * * Parser interface for DOM-based parser (libxml) rather than * stream-based SAX-type parser @@ -24,28 +24,7 @@ PG_MODULE_MAGIC; -/* declarations */ - -static void *pgxml_palloc(size_t size); -static void *pgxml_repalloc(void *ptr, size_t size); -static void pgxml_pfree(void *ptr); -static char *pgxml_pstrdup(const char *string); -static void pgxml_errorHandler(void *ctxt, const char *msg,...); - -void elog_error(int level, char *explain, int force); -void pgxml_parser_init(void); - -static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset, - xmlChar *toptagname, xmlChar *septagname, - xmlChar *plainsep); - -text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, - xmlChar *septag, xmlChar *plainsep); - -xmlChar *pgxml_texttoxmlchar(text *textstring); - -static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath); - +/* externally accessible functions */ Datum xml_is_well_formed(PG_FUNCTION_ARGS); Datum xml_encode_special_chars(PG_FUNCTION_ARGS); @@ -56,111 +35,98 @@ Datum xpath_bool(PG_FUNCTION_ARGS); Datum xpath_list(PG_FUNCTION_ARGS); Datum xpath_table(PG_FUNCTION_ARGS); -/* Global variables */ -char *errbuf; /* per line error buffer */ -char *pgxml_errorMsg = NULL; /* overall error message */ +/* these are exported for use by xslt_proc.c */ -#define ERRBUF_SIZE 200 +void elog_error(const char *explain, bool force); +void pgxml_parser_init(void); -/* memory handling passthrough functions (e.g. palloc, pstrdup are - currently macros, and the others might become so...) */ +/* local declarations */ -static void * -pgxml_palloc(size_t size) -{ -/* elog(DEBUG1,"Alloc %d in CMC %p",size,CurrentMemoryContext); */ - return palloc(size); -} +static void pgxml_errorHandler(void *ctxt, const char *msg,...); -static void * -pgxml_repalloc(void *ptr, size_t size) -{ -/* elog(DEBUG1,"ReAlloc in CMC %p",CurrentMemoryContext);*/ - return repalloc(ptr, size); -} +static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset, + xmlChar *toptagname, xmlChar *septagname, + xmlChar *plainsep); -static void -pgxml_pfree(void *ptr) -{ -/* elog(DEBUG1,"Free in CMC %p",CurrentMemoryContext); */ - pfree(ptr); -} +static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, + xmlChar *septag, xmlChar *plainsep); -static char * -pgxml_pstrdup(const char *string) -{ - return pstrdup(string); -} +static xmlChar *pgxml_texttoxmlchar(text *textstring); -/* The error handling function. This formats an error message and sets +static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath); + +/* Global variables */ +static char *pgxml_errorMsg = NULL; /* overall error message */ + + +/* + * The error handling function. This formats an error message and sets * a flag - an ereport will be issued prior to return */ - static void pgxml_errorHandler(void *ctxt, const char *msg,...) { + char errbuf[1024]; /* per line error buffer */ va_list args; + /* Format the message */ va_start(args, msg); - vsnprintf(errbuf, ERRBUF_SIZE, msg, args); + vsnprintf(errbuf, sizeof(errbuf), msg, args); va_end(args); - /* Now copy the argument across */ + /* Store in, or append to, pgxml_errorMsg */ if (pgxml_errorMsg == NULL) pgxml_errorMsg = pstrdup(errbuf); else { - int32 xsize = strlen(pgxml_errorMsg); - - pgxml_errorMsg = repalloc(pgxml_errorMsg, - (size_t) (xsize + strlen(errbuf) + 1)); - strncpy(&pgxml_errorMsg[xsize - 1], errbuf, strlen(errbuf)); - pgxml_errorMsg[xsize + strlen(errbuf) - 1] = '\0'; + size_t oldsize = strlen(pgxml_errorMsg); + size_t newsize = strlen(errbuf); + /* + * We intentionally discard the last char of the existing message, + * which should be a carriage return. (XXX wouldn't it be saner + * to keep it?) + */ + pgxml_errorMsg = repalloc(pgxml_errorMsg, oldsize + newsize); + memcpy(&pgxml_errorMsg[oldsize - 1], errbuf, newsize); + pgxml_errorMsg[oldsize + newsize - 1] = '\0'; } - memset(errbuf, 0, ERRBUF_SIZE); } -/* This function reports the current message at the level specified */ +/* + * This function ereports the current message if any. If force is true + * then an error is thrown even if pgxml_errorMsg hasn't been set. + */ void -elog_error(int level, char *explain, int force) +elog_error(const char *explain, bool force) { - if (force || (pgxml_errorMsg != NULL)) + if (force || pgxml_errorMsg != NULL) { if (pgxml_errorMsg == NULL) - { - ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), - errmsg(explain))); - } + ereport(ERROR, + (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), + errmsg("%s", explain))); else - { - ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), - errmsg("%s:%s", explain, pgxml_errorMsg))); - pfree(pgxml_errorMsg); - } + ereport(ERROR, + (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), + errmsg("%s: %s", explain, pgxml_errorMsg))); } } +/* + * Initialize for xml parsing. + */ void -pgxml_parser_init() +pgxml_parser_init(void) { - /* - * This code could also set parser settings from user-supplied info. - * Quite how these settings are made is another matter :) - */ + /* Set up error handling */ + pgxml_errorMsg = NULL; + xmlSetGenericErrorFunc(NULL, pgxml_errorHandler); - xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup); + /* Initialize libxml */ xmlInitParser(); - xmlSetGenericErrorFunc(NULL, pgxml_errorHandler); - xmlSubstituteEntitiesDefault(1); xmlLoadExtDtdDefaultValue = 1; - - pgxml_errorMsg = NULL; - - errbuf = palloc(200); - memset(errbuf, 0, 200); - } @@ -171,10 +137,9 @@ PG_FUNCTION_INFO_V1(xml_is_well_formed); Datum xml_is_well_formed(PG_FUNCTION_ARGS) { - /* called as xml_is_well_formed(document) */ - xmlDocPtr doctree; text *t = PG_GETARG_TEXT_P(0); /* document buffer */ int32 docsize = VARSIZE(t) - VARHDRSZ; + xmlDocPtr doctree; pgxml_parser_init(); @@ -184,8 +149,8 @@ xml_is_well_formed(PG_FUNCTION_ARGS) xmlCleanupParser(); PG_RETURN_BOOL(false); /* i.e. not well-formed */ } - xmlCleanupParser(); xmlFreeDoc(doctree); + xmlCleanupParser(); PG_RETURN_BOOL(true); } @@ -215,28 +180,23 @@ xml_encode_special_chars(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(tout); } -static xmlChar - * +/* + * Function translates a nodeset into a text representation + * + * iterates over each node in the set and calls xmlNodeDump to write it to + * an xmlBuffer -from which an xmlChar * string is returned. + * + * each representation is surrounded by ... + * + * plainsep is an ordinary (not tag) separator - if used, then nodes are + * cast to string as output method + */ +static xmlChar * pgxmlNodeSetToText(xmlNodeSetPtr nodeset, xmlChar *toptagname, xmlChar *septagname, xmlChar *plainsep) { - /* Function translates a nodeset into a text representation */ - - /* - * iterates over each node in the set and calls xmlNodeDump to write it to - * an xmlBuffer -from which an xmlChar * string is returned. - */ - - /* each representation is surrounded by ... */ - - /* - * plainsep is an ordinary (not tag) seperator - if used, then nodes are - * cast to string as output method - */ - - xmlBufferPtr buf; xmlChar *result; int i; @@ -253,7 +213,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, { for (i = 0; i < nodeset->nodeNr; i++) { - if (plainsep != NULL) { xmlBufferWriteCHAR(buf, @@ -265,8 +224,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, } else { - - if ((septagname != NULL) && (xmlStrlen(septagname) > 0)) { xmlBufferWriteChar(buf, "<"); @@ -303,19 +260,18 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, /* Translate a PostgreSQL "varlena" -i.e. a variable length parameter * into the libxml2 representation */ - -xmlChar * +static xmlChar * pgxml_texttoxmlchar(text *textstring) { return (xmlChar *) text_to_cstring(textstring); } -/* Public visible XPath functions */ +/* Publicly visible XPath functions */ -/* This is a "raw" xpath function. Check that it returns child elements +/* + * This is a "raw" xpath function. Check that it returns child elements * properly */ - PG_FUNCTION_INFO_V1(xpath_nodeset); Datum @@ -325,8 +281,7 @@ xpath_nodeset(PG_FUNCTION_ARGS) *toptag, *septag; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -339,8 +294,7 @@ xpath_nodeset(PG_FUNCTION_ARGS) xpath = pgxml_texttoxmlchar(xpathsupp); - xpres = pgxml_result_to_text( - pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), + xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), toptag, septag, NULL); /* xmlCleanupParser(); done by result_to_text routine */ @@ -351,9 +305,10 @@ xpath_nodeset(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(xpres); } -/* The following function is almost identical, but returns the elements in */ -/* a list. */ - +/* + * The following function is almost identical, but returns the elements in + * a list. + */ PG_FUNCTION_INFO_V1(xpath_list); Datum @@ -362,8 +317,7 @@ xpath_list(PG_FUNCTION_ARGS) xmlChar *xpath, *plainsep; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -375,8 +329,7 @@ xpath_list(PG_FUNCTION_ARGS) xpath = pgxml_texttoxmlchar(xpathsupp); - xpres = pgxml_result_to_text( - pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), + xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), NULL, NULL, plainsep); /* xmlCleanupParser(); done by result_to_text routine */ @@ -395,8 +348,7 @@ xpath_string(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -416,8 +368,7 @@ xpath_string(PG_FUNCTION_ARGS) xpath[pathsize + 7] = ')'; xpath[pathsize + 8] = '\0'; - xpres = pgxml_result_to_text( - pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), + xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), NULL, NULL, NULL); xmlCleanupParser(); @@ -436,9 +387,7 @@ xpath_number(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp; - + text *xpathsupp; float4 fRes; xmlXPathObjectPtr res; @@ -465,7 +414,6 @@ xpath_number(PG_FUNCTION_ARGS) PG_RETURN_NULL(); PG_RETURN_FLOAT4(fRes); - } @@ -476,9 +424,7 @@ xpath_bool(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp; - + text *xpathsupp; int bRes; xmlXPathObjectPtr res; @@ -502,26 +448,21 @@ xpath_bool(PG_FUNCTION_ARGS) bRes = xmlXPathCastToBoolean(res); xmlCleanupParser(); PG_RETURN_BOOL(bRes); - } /* Core function to evaluate XPath query */ -xmlXPathObjectPtr +static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath) { - xmlDocPtr doctree; xmlXPathContextPtr ctxt; xmlXPathObjectPtr res; - xmlXPathCompExprPtr comppath; - int32 docsize; - docsize = VARSIZE(document) - VARHDRSZ; pgxml_parser_init(); @@ -535,16 +476,13 @@ pgxml_xpath(text *document, xmlChar *xpath) ctxt = xmlXPathNewContext(doctree); ctxt->node = xmlDocGetRootElement(doctree); - /* compile the path */ comppath = xmlXPathCompile(xpath); if (comppath == NULL) { xmlCleanupParser(); xmlFreeDoc(doctree); - elog_error(ERROR, "XPath Syntax Error", 1); - - return NULL; + elog_error("XPath Syntax Error", true); } /* Now evaluate the path expression. */ @@ -563,8 +501,7 @@ pgxml_xpath(text *document, xmlChar *xpath) return res; } -text - * +static text * pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, xmlChar *septag, @@ -595,7 +532,6 @@ pgxml_result_to_text(xmlXPathObjectPtr res, xpresstr = xmlStrdup((const xmlChar *) ""); } - /* Now convert this result back to text */ xpres = cstring_to_text((char *) xpresstr); @@ -605,27 +541,33 @@ pgxml_result_to_text(xmlXPathObjectPtr res, xmlFree(xpresstr); - elog_error(ERROR, "XPath error", 0); - + elog_error("XPath error", false); return xpres; } -/* xpath_table is a table function. It needs some tidying (as do the +/* + * xpath_table is a table function. It needs some tidying (as do the * other functions here! */ - PG_FUNCTION_INFO_V1(xpath_table); Datum xpath_table(PG_FUNCTION_ARGS) { -/* SPI (input tuple) support */ + /* Function parameters */ + char *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0)); + char *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1)); + char *relname = text_to_cstring(PG_GETARG_TEXT_PP(2)); + char *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3)); + char *condition = text_to_cstring(PG_GETARG_TEXT_PP(4)); + + /* SPI (input tuple) support */ SPITupleTable *tuptable; HeapTuple spi_tuple; TupleDesc spi_tupdesc; -/* Output tuple (tuplestore) support */ + /* Output tuple (tuplestore) support */ Tuplestorestate *tupstore = NULL; TupleDesc ret_tupdesc; HeapTuple ret_tuple; @@ -635,13 +577,6 @@ xpath_table(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; -/* Function parameters */ - char *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0)); - char *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1)); - char *relname = text_to_cstring(PG_GETARG_TEXT_PP(2)); - char *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3)); - char *condition = text_to_cstring(PG_GETARG_TEXT_PP(4)); - char **values; xmlChar **xpaths; char *pos; @@ -654,8 +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 */ @@ -682,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); @@ -699,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 @@ -714,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); @@ -743,30 +683,28 @@ xpath_table(PG_FUNCTION_ARGS) pkeyfield, xmlfield, relname, - condition - ); - + condition); if ((ret = SPI_connect()) < 0) elog(ERROR, "xpath_table: SPI_connect returned %d", ret); if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT) - elog(ERROR, "xpath_table: SPI execution failed for query %s", query_buf.data); + elog(ERROR, "xpath_table: SPI execution failed for query %s", + query_buf.data); proc = SPI_processed; /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */ tuptable = SPI_tuptable; spi_tupdesc = tuptable->tupdesc; -/* Switch out of SPI context */ + /* 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. - */ - + /* + * 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), @@ -774,10 +712,12 @@ xpath_table(PG_FUNCTION_ARGS) 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. - */ + /* + * 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 */ @@ -785,13 +725,10 @@ xpath_table(PG_FUNCTION_ARGS) { char *pkey; char *xmldoc; - xmlDocPtr doctree; xmlXPathContextPtr ctxt; xmlXPathObjectPtr res; xmlChar *resstr; - - xmlXPathCompExprPtr comppath; /* Extract the row data as C Strings */ @@ -801,10 +738,9 @@ xpath_table(PG_FUNCTION_ARGS) /* * Clear the values array, so that not-well-formed documents return - * NULL in all columns. + * NULL in all columns. Note that this also means that spare columns + * will be NULL. */ - - /* Note that this also means that spare columns will be NULL. */ for (j = 0; j < ret_tupdesc->natts; j++) values[j] = NULL; @@ -832,10 +768,9 @@ 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); ctxt->node = xmlDocGetRootElement(doctree); xmlSetGenericErrorFunc(ctxt, pgxml_errorHandler); @@ -846,10 +781,7 @@ xpath_table(PG_FUNCTION_ARGS) { xmlCleanupParser(); xmlFreeDoc(doctree); - - elog_error(ERROR, "XPath Syntax Error", 1); - - PG_RETURN_NULL(); /* Keep compiler happy */ + elog_error("XPath Syntax Error", true); } /* Now evaluate the path expression. */ @@ -862,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; @@ -882,7 +815,6 @@ xpath_table(PG_FUNCTION_ARGS) resstr = xmlStrdup((const xmlChar *) ""); } - /* * Insert this into the appropriate column in the * result tuple. @@ -891,6 +823,7 @@ xpath_table(PG_FUNCTION_ARGS) } xmlXPathFreeContext(ctxt); } + /* Now add the tuple to the output, if there is one. */ if (had_values) { @@ -900,9 +833,7 @@ xpath_table(PG_FUNCTION_ARGS) } rownr++; - } while (had_values); - } xmlFreeDoc(doctree); @@ -914,7 +845,7 @@ xpath_table(PG_FUNCTION_ARGS) } xmlCleanupParser(); -/* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */ + tuplestore_donestoring(tupstore); SPI_finish(); @@ -929,5 +860,4 @@ xpath_table(PG_FUNCTION_ARGS) * expecting. */ return (Datum) 0; - } diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c index 055bfcc164..98a6257a3d 100644 --- a/contrib/xml2/xslt_proc.c +++ b/contrib/xml2/xslt_proc.c @@ -1,5 +1,5 @@ /* - * $PostgreSQL: pgsql/contrib/xml2/xslt_proc.c,v 1.15.2.1 2009/07/10 00:32:06 tgl Exp $ + * $PostgreSQL: pgsql/contrib/xml2/xslt_proc.c,v 1.15.2.2 2010/03/01 03:41:04 tgl Exp $ * * XSLT processing functions (requiring libxslt) * @@ -27,17 +27,17 @@ #include +/* externally accessible functions */ + +Datum xslt_process(PG_FUNCTION_ARGS); + /* declarations to come from xpath.c */ -extern void elog_error(int level, char *explain, int force); -extern void pgxml_parser_init(); -extern xmlChar *pgxml_texttoxmlchar(text *textstring); +extern void elog_error(const char *explain, bool force); +extern void pgxml_parser_init(void); /* local defs */ static void parse_params(const char **params, text *paramstr); -Datum xslt_process(PG_FUNCTION_ARGS); - - #define MAXPARAMS 20 /* must be even, see parse_params() */ @@ -80,7 +80,7 @@ xslt_process(PG_FUNCTION_ARGS) if (doctree == NULL) { xmlCleanupParser(); - elog_error(ERROR, "error parsing XML document", 0); + elog_error("error parsing XML document", false); PG_RETURN_NULL(); } @@ -94,7 +94,7 @@ xslt_process(PG_FUNCTION_ARGS) { xmlFreeDoc(doctree); xmlCleanupParser(); - elog_error(ERROR, "error parsing stylesheet as XML document", 0); + elog_error("error parsing stylesheet as XML document", false); PG_RETURN_NULL(); } @@ -109,7 +109,7 @@ xslt_process(PG_FUNCTION_ARGS) xmlFreeDoc(doctree); xsltCleanupGlobals(); xmlCleanupParser(); - elog_error(ERROR, "failed to parse stylesheet", 0); + elog_error("failed to parse stylesheet", false); PG_RETURN_NULL(); }