Title: Message
Hi Theo,

There have been 2 major changes between the 7 and 8 versions that affect the coding in xml2.  You need to edit the source code in order for it to compile properly on 7.

First, work_mem has to be changed to SortMem (line 666).  I.e.

    tupstore = tuplestore_begin_heap(true, false, work_mem);

should be changed to:

    tupstore = tuplestore_begin_heap(true, SortMem);

Second, the error reporting framework has changed.

To fix this, you need to change all the ereport coding to use elog.  I.e.

    ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                          errmsg("xpath_table must be called as a table function")));

should be changed to:

    elog(ERROR,"xpath_table must be called as a table function");
I have attached an edited copy of xpath.c with these changes, if you would like to work with it.
 
Regards,
George


----- Original Message -----
From: Theo Galanakis
To: 'pgsql-sql@postgresql.org'
Sent: Sunday, February 20, 2005 11:28 PM
Subject: FW: [SQL] Working with XML.


Hi,
    I have copied all the files manually from http://developer.postgresql.org/docs/pgsql/contrib/ for the xml2 contribution. However I have the following issue when I attempt to compile with gmake:

gcc -I/usr/include/libxml2 -fpic -I. -I../../src/include -D_GNU_SOURCE   -c -o xpath.o xpath.c
xpath.c: In function `xpath_table':
xpath.c:689: `work_mem' undeclared (first use in this function)
xpath.c:689: (Each undeclared identifier is reported only once
xpath.c:689: for each function it appears in.)
gmake: *** [xpath.o] Error 1

I have installed :

    libxml2-devel-2.5.10-1.rpm

What am I doing wrong, or can someone point me to the direction of a binary for XML2 on RedHat ES3, Postgres 7.4.5.

Cheers,   
    Theo
   
/* Parser interface for DOM-based parser (libxml) rather than
   stream-based SAX-type parser */

/*#include "errcodes.h"*/
#include "postgres.h"
#include "fmgr.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "lib/stringinfo.h"

/* libxml includes */

#include <libxml/xpath.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <libxml/xmlerror.h>
#include <libxml/parserInternals.h>

/* 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);


Datum           xml_valid(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 */
char       *errbuf;                             /* per line error buffer */
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); */
        return pfree(ptr);
}

static char *
pgxml_pstrdup(const char *string)
{
        return pstrdup(string);
}

/* The error handling function. This formats an error message and sets
 * a flag - an ereport will be issued prior to return
 */

static void
pgxml_errorHandler(void *ctxt, const char *msg,...)
{
        va_list         args;

        va_start(args, msg);
        vsnprintf(errbuf, ERRBUF_SIZE, msg, args);
        va_end(args);
        /* Now copy the argument across */
        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';

        }
        memset(errbuf, 0, ERRBUF_SIZE);
}

/* This function reports the current message at the level specified */
void
elog_error(int level, char *explain, int force)
{
        if (force || (pgxml_errorMsg != NULL))
        {
                if (pgxml_errorMsg == NULL)
                {
                        /*
                        ereport(level, 
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
                                                        errmsg(explain)));
                        */
                        elog(ERROR,explain);
                }
                else
                {
                        /*
                        ereport(level, 
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
                                                        errmsg("%s:%s", 
explain, pgxml_errorMsg)));
                        pfree(pgxml_errorMsg);
                        */
                        elog(ERROR,"%s:%s", explain, pgxml_errorMsg);
                }
        }
}

void
pgxml_parser_init()
{
        /*
         * 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();

        xmlSetGenericErrorFunc(NULL, pgxml_errorHandler);

        xmlSubstituteEntitiesDefault(1);
        xmlLoadExtDtdDefaultValue = 1;

        pgxml_errorMsg = NULL;

        errbuf = palloc(200);
        memset(errbuf, 0, 200);

}


/* Returns true if document is well-formed */

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;

        pgxml_parser_init();

        doctree = xmlParseMemory((char *) VARDATA(t), docsize);
        if (doctree == NULL)
        {
                xmlCleanupParser();
                PG_RETURN_BOOL(false);  /* i.e. not well-formed */
        }
        xmlCleanupParser();
        xmlFreeDoc(doctree);
        PG_RETURN_BOOL(true);
}


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 <tagname> ... </tagname> */

        /*
         * plainsep is an ordinary (not tag) seperator - if used, then nodes
         * are cast to string as output method
         */


        xmlBufferPtr buf;
        xmlChar    *result;
        int                     i;

        buf = xmlBufferCreate();

        if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
        {
                xmlBufferWriteChar(buf, "<");
                xmlBufferWriteCHAR(buf, toptagname);
                xmlBufferWriteChar(buf, ">");
        }
        if (nodeset != NULL)
        {
                for (i = 0; i < nodeset->nodeNr; i++)
                {

                        if (plainsep != NULL)
                        {
                                xmlBufferWriteCHAR(buf,
                                                  
xmlXPathCastNodeToString(nodeset->nodeTab[i]));

                                /* If this isn't the last entry, write the 
plain sep. */
                                if (i < (nodeset->nodeNr) - 1)
                                        xmlBufferWriteChar(buf, plainsep);
                        }
                        else
                        {


                                if ((septagname != NULL) && 
(xmlStrlen(septagname) > 0))
                                {
                                        xmlBufferWriteChar(buf, "<");
                                        xmlBufferWriteCHAR(buf, septagname);
                                        xmlBufferWriteChar(buf, ">");
                                }
                                xmlNodeDump(buf,
                                                        
nodeset->nodeTab[i]->doc,
                                                        nodeset->nodeTab[i],
                                                        1, 0);

                                if ((septagname != NULL) && 
(xmlStrlen(septagname) > 0))
                                {
                                        xmlBufferWriteChar(buf, "</");
                                        xmlBufferWriteCHAR(buf, septagname);
                                        xmlBufferWriteChar(buf, ">");
                                }
                        }
                }
        }

        if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
        {
                xmlBufferWriteChar(buf, "</");
                xmlBufferWriteCHAR(buf, toptagname);
                xmlBufferWriteChar(buf, ">");
        }
        result = xmlStrdup(buf->content);
        xmlBufferFree(buf);
        return result;
}


/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
 * into the libxml2 representation
 */

xmlChar *
pgxml_texttoxmlchar(text *textstring)
{
        xmlChar    *res;
        int32           txsize;

        txsize = VARSIZE(textstring) - VARHDRSZ;
        res = (xmlChar *) palloc(txsize + 1);
        memcpy((char *) res, VARDATA(textstring), txsize);
        res[txsize] = '\0';
        return res;
}

/* Public visible XPath functions */

/* This is a "raw" xpath function. Check that it returns child elements
 * properly
 */

PG_FUNCTION_INFO_V1(xpath_nodeset);

Datum
xpath_nodeset(PG_FUNCTION_ARGS)
{
        xmlChar    *xpath,
                           *toptag,
                           *septag;
        int32           pathsize;
        text
                           *xpathsupp,
                           *xpres;

        /* 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 */
        pfree((void *) xpath);

        if (xpres == NULL)
                PG_RETURN_NULL();
        PG_RETURN_TEXT_P(xpres);
}

/*      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,
                           *xpres;

        /* PG_GETARG_TEXT_P(0) is document buffer */
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */

        plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));

        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;

        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((void *) xpath);

        if (xpres == NULL)
                PG_RETURN_NULL();
        PG_RETURN_TEXT_P(xpres);
}


PG_FUNCTION_INFO_V1(xpath_string);

Datum
xpath_string(PG_FUNCTION_ARGS)
{
        xmlChar    *xpath;
        int32           pathsize;
        text
                           *xpathsupp,
                           *xpres;

        /* PG_GETARG_TEXT_P(0) is document buffer */
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */

        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;

        /*
         * We encapsulate the supplied path with "string()" = 8 chars + 1 for
         * NUL at end
         */
        /* We could try casting to string using the libxml function? */

        xpath = (xmlChar *) palloc(pathsize + 9);
        memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
        strncpy((char *) xpath, "string(", 7);
        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((void *) xpath);

        if (xpres == NULL)
                PG_RETURN_NULL();
        PG_RETURN_TEXT_P(xpres);
}


PG_FUNCTION_INFO_V1(xpath_number);

Datum
xpath_number(PG_FUNCTION_ARGS)
{
        xmlChar    *xpath;
        int32           pathsize;
        text
                           *xpathsupp;

        float4          fRes;

        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((void *) xpath);

        if (res == NULL)
        {
                xmlCleanupParser();
                PG_RETURN_NULL();
        }

        fRes = xmlXPathCastToNumber(res);
        xmlCleanupParser();
        if (xmlXPathIsNaN(fRes))
                PG_RETURN_NULL();

        PG_RETURN_FLOAT4(fRes);

}


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((void *) xpath);

        if (res == NULL)
        {
                xmlCleanupParser();
                PG_RETURN_BOOL(false);
        }

        bRes = xmlXPathCastToBoolean(res);
        xmlCleanupParser();
        PG_RETURN_BOOL(bRes);

}



/* Core function to evaluate XPath query */

xmlXPathObjectPtr
pgxml_xpath(text *document, xmlChar * xpath)
{

        xmlDocPtr       doctree;
        xmlXPathContextPtr ctxt;
        xmlXPathObjectPtr res;

        xmlXPathCompExprPtr comppath;

        int32           docsize;


        docsize = VARSIZE(document) - VARHDRSZ;

        pgxml_parser_init();

        doctree = xmlParseMemory((char *) VARDATA(document), docsize);
        if (doctree == NULL)
        {                                                       /* not 
well-formed */
                return NULL;
        }

        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;
        }

        /* Now evaluate the path expression. */
        res = xmlXPathCompiledEval(comppath, ctxt);
        xmlXPathFreeCompExpr(comppath);

        if (res == NULL)
        {
                xmlXPathFreeContext(ctxt);
                /* xmlCleanupParser(); */
                xmlFreeDoc(doctree);

                return NULL;
        }
        /* xmlFreeDoc(doctree); */
        return res;
}

text
                   *
pgxml_result_to_text(xmlXPathObjectPtr res,
                                         xmlChar * toptag,
                                         xmlChar * septag,
                                         xmlChar * plainsep)
{
        xmlChar    *xpresstr;
        int32           ressize;
        text       *xpres;

        if (res == NULL)
                return NULL;
        switch (res->type)
        {
                case XPATH_NODESET:
                        xpresstr = pgxmlNodeSetToText(res->nodesetval,
                                                                                
  toptag,
                                                                                
  septag, plainsep);
                        break;

                case XPATH_STRING:
                        xpresstr = xmlStrdup(res->stringval);
                        break;

                default:
                        elog(NOTICE, "Unsupported XQuery result: %d", 
res->type);
                        xpresstr = xmlStrdup("<unsupported/>");
        }


        /* Now convert this result back to text */
        ressize = strlen(xpresstr);
        xpres = (text *) palloc(ressize + VARHDRSZ);
        memcpy(VARDATA(xpres), xpresstr, ressize);
        VARATT_SIZEP(xpres) = ressize + VARHDRSZ;

        /* Free various storage */
        xmlCleanupParser();
        /* xmlFreeDoc(doctree);  -- will die at end of tuple anyway */

        xmlFree(xpresstr);

        elog_error(ERROR, "XPath error", 0);


        return xpres;
}

/* 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 */
        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      **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 */

        StringInfo      querysql;

/* We only have a valid tuple description in table function mode */
        if (rsinfo->expectedDesc == NULL)
        {
                /*ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                          errmsg("xpath_table must be called as a table 
function")));
            */
                elog(ERROR,"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) */

        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.
 * NOTE - Changed work_mem to sortmem - Nov 22 - 04 - GW
 */

        /*tupstore = tuplestore_begin_heap(true, false, work_mem);*/

        tupstore = tuplestore_begin_heap(true, SortMem);

        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);

        /*
         * We want to materialise because it means that we don't have to carry
         * libxml2 parser state between invocations of this function
         */

        /* check to see if caller supports us returning a tuplestore */
        if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
                /*ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                   errmsg("xpath_table requires Materialize mode, but it is not 
"
                                  "allowed in this context")));
            */
                elog(ERROR,"xpath_table requires Materialize mode, but it is 
not "
                                   "allowed in this context");

        /* 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 */

        querysql = makeStringInfo();

        /* Build initial sql statement */
        appendStringInfo(querysql, "SELECT %s, %s FROM %s WHERE %s",
                                         pkeyfield,
                                         xmlfield,
                                         relname,
                                         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);

        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)));
                */
                elog(ERROR,"Expression returning multiple columns is not valid 
in parameter list");
        }

/* 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);

                                        /* 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("<unsupported/>");
                                                }


                                                /*
                                                 * 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;

}
---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faq

Reply via email to