? 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 #include #include + #include 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(""); + } + + + /* + * 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