Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Functions | Variables

xpath.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "executor/spi.h"
#include "fmgr.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/xml.h"
#include <libxml/xpath.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <libxml/xmlerror.h>
#include <libxml/parserInternals.h>
Include dependency graph for xpath.c:

Go to the source code of this file.

Data Structures

struct  xpath_workspace

Functions

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)
PgXmlErrorContextpgxml_parser_init (PgXmlStrictness strictness)
static xmlChar * pgxmlNodeSetToText (xmlNodeSetPtr nodeset, xmlChar *toptagname, xmlChar *septagname, xmlChar *plainsep)
static textpgxml_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, xpath_workspace *workspace)
static void cleanup_workspace (xpath_workspace *workspace)
 PG_FUNCTION_INFO_V1 (xml_is_well_formed)
 PG_FUNCTION_INFO_V1 (xml_encode_special_chars)
 PG_FUNCTION_INFO_V1 (xpath_nodeset)
 PG_FUNCTION_INFO_V1 (xpath_list)
 PG_FUNCTION_INFO_V1 (xpath_string)
 PG_FUNCTION_INFO_V1 (xpath_number)
 PG_FUNCTION_INFO_V1 (xpath_bool)
 PG_FUNCTION_INFO_V1 (xpath_table)

Variables

 PG_MODULE_MAGIC

Function Documentation

static void cleanup_workspace ( xpath_workspace workspace  )  [static]

Definition at line 479 of file xpath.c.

References xpath_workspace::ctxt, xpath_workspace::doctree, and xpath_workspace::res.

Referenced by pgxml_xpath(), xpath_bool(), xpath_list(), xpath_nodeset(), xpath_number(), and xpath_string().

{
    if (workspace->res)
        xmlXPathFreeObject(workspace->res);
    workspace->res = NULL;
    if (workspace->ctxt)
        xmlXPathFreeContext(workspace->ctxt);
    workspace->ctxt = NULL;
    if (workspace->doctree)
        xmlFreeDoc(workspace->doctree);
    workspace->doctree = NULL;
}

PG_FUNCTION_INFO_V1 ( xml_encode_special_chars   ) 
PG_FUNCTION_INFO_V1 ( xpath_nodeset   ) 
PG_FUNCTION_INFO_V1 ( xpath_list   ) 
PG_FUNCTION_INFO_V1 ( xpath_string   ) 
PG_FUNCTION_INFO_V1 ( xpath_number   ) 
PG_FUNCTION_INFO_V1 ( xpath_bool   ) 
PG_FUNCTION_INFO_V1 ( xpath_table   ) 
PG_FUNCTION_INFO_V1 ( xml_is_well_formed   ) 
PgXmlErrorContext * pgxml_parser_init ( PgXmlStrictness  strictness  ) 

Definition at line 77 of file xpath.c.

References pg_xml_init().

Referenced by pgxml_xpath(), xml_is_well_formed(), xpath_table(), and xslt_process().

{
    PgXmlErrorContext *xmlerrcxt;

    /* Set up error handling (we share the core's error handler) */
    xmlerrcxt = pg_xml_init(strictness);

    /* Note: we're assuming an elog cannot be thrown by the following calls */

    /* Initialize libxml */
    xmlInitParser();

    xmlSubstituteEntitiesDefault(1);
    xmlLoadExtDtdDefaultValue = 1;

    return xmlerrcxt;
}

static text * pgxml_result_to_text ( xmlXPathObjectPtr  res,
xmlChar *  toptag,
xmlChar *  septag,
xmlChar *  plainsep 
) [static]

Definition at line 493 of file xpath.c.

References cstring_to_text(), elog, NOTICE, NULL, and pgxmlNodeSetToText().

Referenced by xpath_list(), xpath_nodeset(), and xpath_string().

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

    /* Now convert this result back to text */
    xpres = cstring_to_text((char *) xpresstr);

    /* Free various storage */
    xmlFree(xpresstr);

    return xpres;
}

static xmlChar * pgxml_texttoxmlchar ( text textstring  )  [static]

Definition at line 247 of file xpath.c.

References text_to_cstring().

Referenced by xml_encode_special_chars(), xpath_bool(), xpath_list(), xpath_nodeset(), and xpath_number().

{
    return (xmlChar *) text_to_cstring(textstring);
}

static xmlXPathObjectPtr pgxml_xpath ( text document,
xmlChar *  xpath,
xpath_workspace workspace 
) [static]

Definition at line 426 of file xpath.c.

References cleanup_workspace(), xpath_workspace::ctxt, xpath_workspace::doctree, ERROR, NULL, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pg_xml_done(), PG_XML_STRICTNESS_LEGACY, pgxml_parser_init(), xpath_workspace::res, VARDATA, VARSIZE, and xml_ereport().

Referenced by xpath_bool(), xpath_list(), xpath_nodeset(), xpath_number(), and xpath_string().

{
    int32       docsize = VARSIZE(document) - VARHDRSZ;
    PgXmlErrorContext *xmlerrcxt;
    xmlXPathCompExprPtr comppath;

    workspace->doctree = NULL;
    workspace->ctxt = NULL;
    workspace->res = NULL;

    xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);

    PG_TRY();
    {
        workspace->doctree = xmlParseMemory((char *) VARDATA(document),
                                            docsize);
        if (workspace->doctree != NULL)
        {
            workspace->ctxt = xmlXPathNewContext(workspace->doctree);
            workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);

            /* compile the path */
            comppath = xmlXPathCompile(xpath);
            if (comppath == NULL)
                xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
                            "XPath Syntax Error");

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

            xmlXPathFreeCompExpr(comppath);
        }
    }
    PG_CATCH();
    {
        cleanup_workspace(workspace);

        pg_xml_done(xmlerrcxt, true);

        PG_RE_THROW();
    }
    PG_END_TRY();

    if (workspace->res == NULL)
        cleanup_workspace(workspace);

    pg_xml_done(xmlerrcxt, false);

    return workspace->res;
}

static xmlChar * pgxmlNodeSetToText ( xmlNodeSetPtr  nodeset,
xmlChar *  toptagname,
xmlChar *  septagname,
xmlChar *  plainsep 
) [static]

Definition at line 178 of file xpath.c.

References buf, i, and NULL.

Referenced by pgxml_result_to_text().

{
    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, (char *) 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;
}

Datum xml_encode_special_chars ( PG_FUNCTION_ARGS   ) 

Definition at line 146 of file xpath.c.

References cstring_to_text(), NULL, pfree(), PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, and pgxml_texttoxmlchar().

{
    text       *tin = PG_GETARG_TEXT_P(0);
    text       *tout;
    xmlChar    *ts,
               *tt;

    ts = pgxml_texttoxmlchar(tin);

    tt = xmlEncodeSpecialChars(NULL, ts);

    pfree(ts);

    tout = cstring_to_text((char *) tt);

    xmlFree(tt);

    PG_RETURN_TEXT_P(tout);
}

Datum xml_is_well_formed ( PG_FUNCTION_ARGS   ) 

Definition at line 108 of file xpath.c.

{
    text       *t = PG_GETARG_TEXT_P(0);        /* document buffer */
    bool        result = false;
    int32       docsize = VARSIZE(t) - VARHDRSZ;
    xmlDocPtr   doctree;
    PgXmlErrorContext *xmlerrcxt;

    xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);

    PG_TRY();
    {
        doctree = xmlParseMemory((char *) VARDATA(t), docsize);

        result = (doctree != NULL);

        if (doctree != NULL)
            xmlFreeDoc(doctree);
    }
    PG_CATCH();
    {
        pg_xml_done(xmlerrcxt, true);

        PG_RE_THROW();
    }
    PG_END_TRY();

    pg_xml_done(xmlerrcxt, false);

    PG_RETURN_BOOL(result);
}

Datum xpath_bool ( PG_FUNCTION_ARGS   ) 

Definition at line 396 of file xpath.c.

References cleanup_workspace(), NULL, pfree(), PG_GETARG_TEXT_P, PG_RETURN_BOOL, pgxml_texttoxmlchar(), and pgxml_xpath().

{
    text       *document = PG_GETARG_TEXT_P(0);
    text       *xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
    xmlChar    *xpath;
    int         bRes;
    xmlXPathObjectPtr res;
    xpath_workspace workspace;

    xpath = pgxml_texttoxmlchar(xpathsupp);

    res = pgxml_xpath(document, xpath, &workspace);

    pfree(xpath);

    if (res == NULL)
        PG_RETURN_BOOL(false);

    bRes = xmlXPathCastToBoolean(res);

    cleanup_workspace(&workspace);

    PG_RETURN_BOOL(bRes);
}

Datum xpath_list ( PG_FUNCTION_ARGS   ) 

Definition at line 294 of file xpath.c.

References cleanup_workspace(), NULL, pfree(), PG_GETARG_TEXT_P, PG_RETURN_NULL, PG_RETURN_TEXT_P, pgxml_result_to_text(), pgxml_texttoxmlchar(), and pgxml_xpath().

{
    text       *document = PG_GETARG_TEXT_P(0);
    text       *xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
    xmlChar    *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
    xmlChar    *xpath;
    text       *xpres;
    xmlXPathObjectPtr res;
    xpath_workspace workspace;

    xpath = pgxml_texttoxmlchar(xpathsupp);

    res = pgxml_xpath(document, xpath, &workspace);

    xpres = pgxml_result_to_text(res, NULL, NULL, plainsep);

    cleanup_workspace(&workspace);

    pfree(xpath);

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

Datum xpath_nodeset ( PG_FUNCTION_ARGS   ) 

Definition at line 261 of file xpath.c.

References cleanup_workspace(), NULL, pfree(), PG_GETARG_TEXT_P, PG_RETURN_NULL, PG_RETURN_TEXT_P, pgxml_result_to_text(), pgxml_texttoxmlchar(), and pgxml_xpath().

{
    text       *document = PG_GETARG_TEXT_P(0);
    text       *xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
    xmlChar    *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
    xmlChar    *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
    xmlChar    *xpath;
    text       *xpres;
    xmlXPathObjectPtr res;
    xpath_workspace workspace;

    xpath = pgxml_texttoxmlchar(xpathsupp);

    res = pgxml_xpath(document, xpath, &workspace);

    xpres = pgxml_result_to_text(res, toptag, septag, NULL);

    cleanup_workspace(&workspace);

    pfree(xpath);

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

Datum xpath_number ( PG_FUNCTION_ARGS   ) 

Definition at line 364 of file xpath.c.

References cleanup_workspace(), NULL, pfree(), PG_GETARG_TEXT_P, PG_RETURN_FLOAT4, PG_RETURN_NULL, pgxml_texttoxmlchar(), and pgxml_xpath().

{
    text       *document = PG_GETARG_TEXT_P(0);
    text       *xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
    xmlChar    *xpath;
    float4      fRes;
    xmlXPathObjectPtr res;
    xpath_workspace workspace;

    xpath = pgxml_texttoxmlchar(xpathsupp);

    res = pgxml_xpath(document, xpath, &workspace);

    pfree(xpath);

    if (res == NULL)
        PG_RETURN_NULL();

    fRes = xmlXPathCastToNumber(res);

    cleanup_workspace(&workspace);

    if (xmlXPathIsNaN(fRes))
        PG_RETURN_NULL();

    PG_RETURN_FLOAT4(fRes);
}

Datum xpath_string ( PG_FUNCTION_ARGS   ) 

Definition at line 323 of file xpath.c.

References cleanup_workspace(), NULL, palloc(), pfree(), PG_GETARG_TEXT_P, PG_RETURN_NULL, PG_RETURN_TEXT_P, pgxml_result_to_text(), pgxml_xpath(), VARDATA, and VARSIZE.

{
    text       *document = PG_GETARG_TEXT_P(0);
    text       *xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
    xmlChar    *xpath;
    int32       pathsize;
    text       *xpres;
    xmlXPathObjectPtr res;
    xpath_workspace workspace;

    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);
    strncpy((char *) xpath, "string(", 7);
    memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
    xpath[pathsize + 7] = ')';
    xpath[pathsize + 8] = '\0';

    res = pgxml_xpath(document, xpath, &workspace);

    xpres = pgxml_result_to_text(res, NULL, NULL, NULL);

    cleanup_workspace(&workspace);

    pfree(xpath);

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

Datum xpath_table ( PG_FUNCTION_ARGS   ) 

Definition at line 537 of file xpath.c.

References ReturnSetInfo::allowedModes, appendStringInfo(), BuildTupleFromCStrings(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, heap_freetuple(), i, initStringInfo(), IsA, MemoryContextSwitchTo(), tupleDesc::natts, NOTICE, NULL, palloc(), pfree(), PG_CATCH, PG_END_TRY, PG_GETARG_TEXT_PP, PG_RE_THROW, PG_TRY, pg_xml_done(), PG_XML_STRICTNESS_LEGACY, pgxml_parser_init(), ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, SPI_connect(), SPI_exec(), SPI_finish(), SPI_getvalue(), SPI_OK_SELECT, SPI_processed, SPI_tuptable, text_to_cstring(), SPITupleTable::tupdesc, TupleDescGetAttInMetadata(), tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_puttuple(), SPITupleTable::vals, values, work_mem, and xml_ereport().

{
    /* Function parameters */
    char       *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char       *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1));
    char       *relname = text_to_cstring(PG_GETARG_TEXT_PP(2));
    char       *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3));
    char       *condition = text_to_cstring(PG_GETARG_TEXT_PP(4));

    /* SPI (input tuple) support */
    SPITupleTable *tuptable;
    HeapTuple   spi_tuple;
    TupleDesc   spi_tupdesc;

    /* Output tuple (tuplestore) support */
    Tuplestorestate *tupstore = NULL;
    TupleDesc   ret_tupdesc;
    HeapTuple   ret_tuple;

    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    AttInMetadata *attinmeta;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;

    char      **values;
    xmlChar   **xpaths;
    char       *pos;
    const char *pathsep = "|";

    int         numpaths;
    int         ret;
    int         proc;
    int         i;
    int         j;
    int         rownr;          /* For issuing multiple rows from one original
                                 * document */
    bool        had_values;     /* To determine end of nodeset results */
    StringInfoData query_buf;
    PgXmlErrorContext *xmlerrcxt;
    volatile xmlDocPtr doctree = NULL;

    /* 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 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 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(rsinfo->allowedModes & SFRM_Materialize_Random,
                              false, work_mem);

    MemoryContextSwitchTo(oldcontext);

    /* 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
     * 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;
    while (numpaths < (ret_tupdesc->natts - 1))
    {
        xpaths[numpaths++] = (xmlChar *) pos;
        pos = strstr(pos, pathsep);
        if (pos != NULL)
        {
            *pos = '\0';
            pos++;
        }
        else
            break;
    }

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

    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.  This should happen after we are done evaluating the
     * query, in case it calls functions that set up libxml differently.
     */
    xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);

    PG_TRY();
    {
        /* For each row i.e. document returned from SPI */
        for (i = 0; i < proc; i++)
        {
            char       *pkey;
            char       *xmldoc;
            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 */
            if (xmldoc)
                doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
            else    /* treat NULL as not well-formed */
                doctree = NULL;

            if (doctree == NULL)
            {
                /* not well-formed, so output all-NULL tuple */
                ret_tuple = BuildTupleFromCStrings(attinmeta, values);
                tuplestore_puttuple(tupstore, ret_tuple);
                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 = false;
                    for (j = 0; j < numpaths; j++)
                    {
                        ctxt = xmlXPathNewContext(doctree);
                        ctxt->node = xmlDocGetRootElement(doctree);

                        /* compile the path */
                        comppath = xmlXPathCompile(xpaths[j]);
                        if (comppath == NULL)
                            xml_ereport(xmlerrcxt, ERROR,
                                        ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
                                        "XPath Syntax Error");

                        /* 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 = true;
                                    }
                                    else
                                        resstr = NULL;

                                    break;

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

                                default:
                                    elog(NOTICE, "unsupported XQuery result: %d", res->type);
                                    resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
                            }

                            /*
                             * Insert this into the appropriate column in the
                             * result tuple.
                             */
                            values[j + 1] = (char *) resstr;
                        }
                        xmlXPathFreeContext(ctxt);
                    }

                    /* Now add the tuple to the output, if there is one. */
                    if (had_values)
                    {
                        ret_tuple = BuildTupleFromCStrings(attinmeta, values);
                        tuplestore_puttuple(tupstore, ret_tuple);
                        heap_freetuple(ret_tuple);
                    }

                    rownr++;
                } while (had_values);
            }

            if (doctree != NULL)
                xmlFreeDoc(doctree);
            doctree = NULL;

            if (pkey)
                pfree(pkey);
            if (xmldoc)
                pfree(xmldoc);
        }
    }
    PG_CATCH();
    {
        if (doctree != NULL)
            xmlFreeDoc(doctree);

        pg_xml_done(xmlerrcxt, true);

        PG_RE_THROW();
    }
    PG_END_TRY();

    if (doctree != NULL)
        xmlFreeDoc(doctree);

    pg_xml_done(xmlerrcxt, false);

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


Variable Documentation

Definition at line 27 of file xpath.c.