From 2502898d73fbb9e8ed3ef761940777d9c7fb013d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 1 Mar 2010 03:41:17 +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 | 142 +++++++++++++ contrib/xml2/sql/xml2.sql | 94 ++++++++ contrib/xml2/xpath.c | 377 +++++++++++++-------------------- contrib/xml2/xslt_proc.c | 23 +- 5 files changed, 400 insertions(+), 237 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 e9ed20f128c..c247fca3005 100644 --- a/contrib/xml2/Makefile +++ b/contrib/xml2/Makefile @@ -10,6 +10,7 @@ SHLIB_LINK = -lxslt -lxml2 DATA_built = pgxml.sql DATA = uninstall_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..019009cedda --- /dev/null +++ b/contrib/xml2/expected/xml2.out @@ -0,0 +1,142 @@ +-- +-- 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'); +create index idx_xpath on t1 ( xpath_string +('/attributes/attribute[@name="attr_1"]/text()', xml_data)); diff --git a/contrib/xml2/sql/xml2.sql b/contrib/xml2/sql/xml2.sql new file mode 100644 index 00000000000..f23aed77811 --- /dev/null +++ b/contrib/xml2/sql/xml2.sql @@ -0,0 +1,94 @@ +-- +-- 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'); + +create index idx_xpath on t1 ( xpath_string +('/attributes/attribute[@name="attr_1"]/text()', xml_data)); diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 548d61249d1..4151398a1f6 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -19,28 +19,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); @@ -51,116 +30,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 %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 +/* + * 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); - } @@ -171,10 +134,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 +146,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); } @@ -219,28 +181,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; @@ -257,7 +214,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, { for (i = 0; i < nodeset->nodeNr; i++) { - if (plainsep != NULL) { xmlBufferWriteCHAR(buf, @@ -269,8 +225,6 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, } else { - - if ((septagname != NULL) && (xmlStrlen(septagname) > 0)) { xmlBufferWriteChar(buf, "<"); @@ -307,8 +261,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; @@ -321,12 +274,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 @@ -336,8 +289,7 @@ xpath_nodeset(PG_FUNCTION_ARGS) *toptag, *septag; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -350,8 +302,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 */ @@ -362,9 +313,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 @@ -373,8 +325,7 @@ xpath_list(PG_FUNCTION_ARGS) xmlChar *xpath, *plainsep; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -386,8 +337,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 */ @@ -406,8 +356,7 @@ xpath_string(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp, + text *xpathsupp, *xpres; /* PG_GETARG_TEXT_P(0) is document buffer */ @@ -427,8 +376,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(); @@ -447,9 +395,7 @@ xpath_number(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp; - + text *xpathsupp; float4 fRes; xmlXPathObjectPtr res; @@ -476,7 +422,6 @@ xpath_number(PG_FUNCTION_ARGS) PG_RETURN_NULL(); PG_RETURN_FLOAT4(fRes); - } @@ -487,9 +432,7 @@ xpath_bool(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; - text - *xpathsupp; - + text *xpathsupp; int bRes; xmlXPathObjectPtr res; @@ -513,26 +456,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(); @@ -546,16 +484,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. */ @@ -574,12 +509,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; @@ -607,7 +541,6 @@ pgxml_result_to_text(xmlXPathObjectPtr res, xpresstr = xmlStrdup(""); } - /* Now convert this result back to text */ ressize = strlen(xpresstr); xpres = (text *) palloc(ressize + VARHDRSZ); @@ -620,27 +553,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; @@ -650,13 +589,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; @@ -669,8 +601,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 */ @@ -697,7 +628,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); @@ -712,6 +642,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 @@ -727,26 +663,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] = 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); @@ -756,30 +693,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), @@ -787,10 +722,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 */ @@ -798,13 +735,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 */ @@ -814,10 +748,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; @@ -845,10 +778,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); @@ -859,10 +791,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. */ @@ -875,11 +804,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; @@ -895,7 +825,6 @@ xpath_table(PG_FUNCTION_ARGS) resstr = xmlStrdup(""); } - /* * Insert this into the appropriate column in the * result tuple. @@ -904,6 +833,7 @@ xpath_table(PG_FUNCTION_ARGS) } xmlXPathFreeContext(ctxt); } + /* Now add the tuple to the output, if there is one. */ if (had_values) { @@ -913,9 +843,7 @@ xpath_table(PG_FUNCTION_ARGS) } rownr++; - } while (had_values); - } xmlFreeDoc(doctree); @@ -927,7 +855,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(); @@ -942,5 +870,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 ab0caaa998e..a38ab5a7a87 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(); }