From 5cdd478bd08922f963dae32e47bcb2ef8f0836af Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 1 Mar 2010 03:41:29 +0000 Subject: [PATCH] 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. --- contrib/xml2/Makefile | 1 + contrib/xml2/expected/xml2.out | 147 ++++++++++++ contrib/xml2/sql/xml2.sql | 93 ++++++++ contrib/xml2/xpath.c | 398 ++++++++++++++------------------- contrib/xml2/xslt_proc.c | 23 +- 5 files changed, 415 insertions(+), 247 deletions(-) create mode 100644 contrib/xml2/expected/xml2.out create mode 100644 contrib/xml2/sql/xml2.sql diff --git a/contrib/xml2/Makefile b/contrib/xml2/Makefile index 15187eacd15..13b9cb447ec 100644 --- a/contrib/xml2/Makefile +++ b/contrib/xml2/Makefile @@ -9,6 +9,7 @@ OBJS = xpath.o xslt_proc.o SHLIB_LINK = -lxslt -lxml2 DATA_built = pgxml.sql +REGRESS = xml2 DOCS = README.xml2 PG_CPPFLAGS := $(shell xml2-config --cflags) diff --git a/contrib/xml2/expected/xml2.out b/contrib/xml2/expected/xml2.out new file mode 100644 index 00000000000..45348be8123 --- /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 xslt_process( +' + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + +
'::text, +$$ + + + + + + + + + + + +$$::text); + xslt_process +----------------------- + + + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + +
+ +(1 row) + +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 text, 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('',$$ + + + + + +$$); + xslt_process +----------------------- + + + +(1 row) + +select xslt_process('',$$ + + + + + +$$); + xslt_process +----------------------- + + + +(1 row) + +create table t1 (id integer, xml_data text); +insert into t1 (id, xml_data) +values +(1, 'Some +Value'); +select xpath_string('/attributes/attribute[@name="attr_1"]/text()', xml_data) +from t1; + xpath_string +-------------- + +(1 row) + diff --git a/contrib/xml2/sql/xml2.sql b/contrib/xml2/sql/xml2.sql new file mode 100644 index 00000000000..57e718890c9 --- /dev/null +++ b/contrib/xml2/sql/xml2.sql @@ -0,0 +1,93 @@ +-- +-- 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 xslt_process( +' + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + +
'::text, +$$ + + + + + + + + + + + +$$::text); + +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 text, 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('',$$ + + + + + +$$); + +select xslt_process('',$$ + + + + + +$$); + +create table t1 (id integer, xml_data text); +insert into t1 (id, xml_data) +values +(1, 'Some +Value'); +select xpath_string('/attributes/attribute[@name="attr_1"]/text()', xml_data) +from t1; diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 268937e38bc..a5e38d9b278 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -16,28 +16,7 @@ #include #include -/* 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_valid(PG_FUNCTION_ARGS); Datum xml_encode_special_chars(PG_FUNCTION_ARGS); @@ -48,116 +27,100 @@ Datum xpath_bool(PG_FUNCTION_ARGS); Datum xpath_list(PG_FUNCTION_ARGS); Datum xpath_table(PG_FUNCTION_ARGS); +/* these are exported for use by xslt_proc.c */ + +void elog_error(const char *explain, bool force); +void pgxml_parser_init(void); + +/* local declarations */ + +static void pgxml_errorHandler(void *ctxt, const char *msg,...); + +static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset, + xmlChar *toptagname, xmlChar *septagname, + xmlChar *plainsep); + +static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, + xmlChar *septag, xmlChar *plainsep); + +static xmlChar *pgxml_texttoxmlchar(text *textstring); + +static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath); + /* Global variables */ -char *errbuf; /* per line error buffer */ -char *pgxml_errorMsg = NULL; /* overall error message */ +static char *pgxml_errorMsg = NULL; /* overall error message */ -/* Convenience macros */ - -#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp))) #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp))) -#define ERRBUF_SIZE 200 -/* memory handling passthrough functions (e.g. palloc, pstrdup are - currently macros, and the others might become so...) */ - -static void * -pgxml_palloc(size_t size) -{ -/* elog(DEBUG1,"Alloc %d in CMC %x",size,CurrentMemoryContext); */ - return palloc(size); -} - -static void * -pgxml_repalloc(void *ptr, size_t size) -{ -/* elog(DEBUG1,"ReAlloc in CMC %x",CurrentMemoryContext);*/ - return repalloc(ptr, size); -} - -static void -pgxml_pfree(void *ptr) -{ -/* elog(DEBUG1,"Free in CMC %x",CurrentMemoryContext); */ - pfree(ptr); -} - -static char * -pgxml_pstrdup(const char *string) -{ - return pstrdup(string); -} - -/* The error handling function. This formats an error message and sets +/* + * 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 :) - */ - - xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup); - xmlInitParser(); - + /* Set up error handling */ + pgxml_errorMsg = NULL; xmlSetGenericErrorFunc(NULL, pgxml_errorHandler); + /* Initialize libxml */ + xmlInitParser(); + xmlSubstituteEntitiesDefault(1); xmlLoadExtDtdDefaultValue = 1; - - pgxml_errorMsg = NULL; - - errbuf = palloc(200); - memset(errbuf, 0, 200); - } @@ -168,10 +131,9 @@ PG_FUNCTION_INFO_V1(xml_valid); Datum xml_valid(PG_FUNCTION_ARGS) { - /* called as xml_valid(document) */ - xmlDocPtr doctree; text *t = PG_GETARG_TEXT_P(0); /* document buffer */ int32 docsize = VARSIZE(t) - VARHDRSZ; + xmlDocPtr doctree; pgxml_parser_init(); @@ -181,8 +143,8 @@ xml_valid(PG_FUNCTION_ARGS) xmlCleanupParser(); PG_RETURN_BOOL(false); /* i.e. not well-formed */ } - xmlCleanupParser(); xmlFreeDoc(doctree); + xmlCleanupParser(); PG_RETURN_BOOL(true); } @@ -215,28 +177,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) + 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 +210,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, { for (i = 0; i < nodeset->nodeNr; i++) { - if (plainsep != NULL) { xmlBufferWriteCHAR(buf, @@ -265,8 +221,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, } else { - - if ((septagname != NULL) && (xmlStrlen(septagname) > 0)) { xmlBufferWriteChar(buf, "<"); @@ -303,8 +257,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, /* Translate a PostgreSQL "varlena" -i.e. a variable length parameter * into the libxml2 representation */ - -xmlChar * +static xmlChar * pgxml_texttoxmlchar(text *textstring) { xmlChar *res; @@ -317,12 +270,12 @@ pgxml_texttoxmlchar(text *textstring) return res; } -/* 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 @@ -332,8 +285,7 @@ xpath_nodeset(PG_FUNCTION_ARGS) *toptag, *septag; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -346,8 +298,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 */ @@ -358,9 +309,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 @@ -369,8 +321,7 @@ xpath_list(PG_FUNCTION_ARGS) xmlChar *xpath, *plainsep; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -382,8 +333,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 */ @@ -402,8 +352,7 @@ xpath_string(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -423,8 +372,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(); @@ -443,9 +391,7 @@ xpath_number(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp; - + text *xpathsupp; float4 fRes; xmlXPathObjectPtr res; @@ -472,7 +418,6 @@ xpath_number(PG_FUNCTION_ARGS) PG_RETURN_NULL(); PG_RETURN_FLOAT4(fRes); - } @@ -483,9 +428,7 @@ xpath_bool(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp; - + text *xpathsupp; int bRes; xmlXPathObjectPtr res; @@ -509,26 +452,21 @@ xpath_bool(PG_FUNCTION_ARGS) bRes = xmlXPathCastToBoolean(res); xmlCleanupParser(); PG_RETURN_BOOL(bRes); - } /* Core function to evaluate XPath query */ -xmlXPathObjectPtr -pgxml_xpath(text *document, xmlChar * xpath) +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(); @@ -542,16 +480,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. */ @@ -570,12 +505,11 @@ pgxml_xpath(text *document, xmlChar * xpath) return res; } -text - * +static text * pgxml_result_to_text(xmlXPathObjectPtr res, - xmlChar * toptag, - xmlChar * septag, - xmlChar * plainsep) + xmlChar *toptag, + xmlChar *septag, + xmlChar *plainsep) { xmlChar *xpresstr; int32 ressize; @@ -603,7 +537,6 @@ pgxml_result_to_text(xmlXPathObjectPtr res, xpresstr = xmlStrdup(""); } - /* Now convert this result back to text */ ressize = strlen(xpresstr); xpres = (text *) palloc(ressize + VARHDRSZ); @@ -616,27 +549,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 = 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)); + + /* 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; @@ -646,13 +585,6 @@ xpath_table(PG_FUNCTION_ARGS) 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 **values; xmlChar **xpaths; xmlChar *pos; @@ -663,11 +595,10 @@ xpath_table(PG_FUNCTION_ARGS) 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 */ - - StringInfo querysql; + int rownr; /* For issuing multiple rows from one original + * document */ + bool had_values; /* To determine end of nodeset results */ + StringInfoData query_buf; /* We only have a valid tuple description in table function mode */ if (rsinfo->expectedDesc == NULL) @@ -676,9 +607,10 @@ xpath_table(PG_FUNCTION_ARGS) errmsg("xpath_table must be called as a table function"))); } -/* The tuplestore must exist in a higher context than - * this function call (per_query_ctx is used) */ - + /* + * 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); @@ -703,6 +635,12 @@ xpath_table(PG_FUNCTION_ARGS) attinmeta = TupleDescGetAttInMetadata(ret_tupdesc); + /* 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"))); + /* * We want to materialise because it means that we don't have to carry * libxml2 parser state between invocations of this function @@ -719,60 +657,58 @@ 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] = 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 */ - - querysql = makeStringInfo(); + initStringInfo(&query_buf); /* Build initial sql statement */ - appendStringInfo(querysql, "SELECT %s, %s FROM %s WHERE %s", + appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s", pkeyfield, xmlfield, relname, - condition - ); - + condition); if ((ret = SPI_connect()) < 0) elog(ERROR, "xpath_table: SPI_connect returned %d", ret); - if ((ret = SPI_exec(querysql->data, 0)) != SPI_OK_SELECT) - elog(ERROR, "xpath_table: SPI execution failed for query %s", querysql->data); + if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT) + elog(ERROR, "xpath_table: SPI execution failed for query %s", + query_buf.data); proc = SPI_processed; /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */ tuptable = SPI_tuptable; spi_tupdesc = tuptable->tupdesc; -/* Switch out of SPI context */ + /* 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), @@ -780,10 +716,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 */ @@ -791,13 +729,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 */ @@ -806,11 +741,10 @@ xpath_table(PG_FUNCTION_ARGS) xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2); /* - * Clear the values array, so that not-well-formed documents - * return NULL in all columns. + * 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. */ - - /* Note that this also means that spare columns will be NULL. */ for (j = 0; j < ret_tupdesc->natts; j++) values[j] = NULL; @@ -840,10 +774,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); @@ -854,10 +787,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. */ @@ -870,11 +800,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; @@ -890,7 +821,6 @@ xpath_table(PG_FUNCTION_ARGS) resstr = xmlStrdup(""); } - /* * Insert this into the appropriate column in the * result tuple. @@ -899,6 +829,7 @@ xpath_table(PG_FUNCTION_ARGS) } xmlXPathFreeContext(ctxt); } + /* Now add the tuple to the output, if there is one. */ if (had_values) { @@ -910,9 +841,7 @@ xpath_table(PG_FUNCTION_ARGS) } rownr++; - } while (had_values); - } xmlFreeDoc(doctree); @@ -924,7 +853,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(); @@ -939,5 +868,4 @@ xpath_table(PG_FUNCTION_ARGS) * verify we did what it was expecting. */ return (Datum) 0; - } diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c index 38c28c6d956..18d9879fcfc 100644 --- a/contrib/xml2/xslt_proc.c +++ b/contrib/xml2/xslt_proc.c @@ -21,22 +21,21 @@ #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); - -#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp))) +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() */ +#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp))) + PG_FUNCTION_INFO_V1(xslt_process); @@ -82,7 +81,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(); } @@ -96,7 +95,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(); } @@ -111,7 +110,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(); }