In case this still matters, I think GetValue should look more or less
like this (untested):


/*
 * Return the value for column number 'colnum' for the current row.  If column
 * -1 is requested, return representation of the whole row.
 *
 * This leaks memory, so be sure to reset often the context in which it's
 * called.
 */
static Datum
XmlTableGetValue(TableExprState *state, int colnum, bool *isnull)
{
#ifdef USE_LIBXML
        XmlTableBuilderData *xtCxt;
        Datum           result = (Datum) 0;
        xmlNodePtr      cur;
        char       *cstr = NULL;
        volatile xmlXPathObjectPtr xpathobj;

        xtCxt = GetXmlTableBuilderPrivateData(state, "XmlTableGetValue");

        Assert(xtCxt->xpathobj &&
                   xtCxt->xpathobj->type == XPATH_NODESET &&
                   xtCxt->xpathobj->nodesetval != NULL);

        /* Propagate context related error context to libxml2 */
        xmlSetStructuredErrorFunc((void *) xtCxt->xmlerrcxt, xml_errorHandler);

        cur = xtCxt->xpathobj->nodesetval->nodeTab[xtCxt->row_count - 1];
        if (cur->type != XML_ELEMENT_NODE)
                elog(ERROR, "unexpected xmlNode type");

        /* Handle whole row case the easy way. */
        if (colnum == -1)
        {
                text       *txt;

                txt = xml_xmlnodetoxmltype(cur, xtCxt->xmlerrcxt);
                result = InputFunctionCall(&state->in_functions[0],
                                                                   
text_to_cstring(txt),
                                                                   
state->typioparams[0],
                                                                   -1);
                *isnull = false;

                return result;
        }

        Assert(xtCxt->xpathscomp[colnum] != NULL);

        xpathobj = NULL;
        PG_TRY();
        {
                Form_pg_attribute attr;

                attr = state->resultSlot->tts_tupleDescriptor->attrs[colnum];

                /* Set current node as entry point for XPath evaluation */
                xmlXPathSetContextNode(cur, xtCxt->xpathcxt);

                /* Evaluate column path */
                xpathobj = xmlXPathCompiledEval(xtCxt->xpathscomp[colnum], 
xtCxt->xpathcxt);
                if (xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
                        xml_ereport(xtCxt->xmlerrcxt, ERROR, 
ERRCODE_INTERNAL_ERROR,
                                                "could not create XPath 
object");

                if (xpathobj->type == XPATH_NODESET)
                {
                        int                     count;
                        Oid                     targettypid = attr->atttypid;

                        if (xpathobj->nodesetval != NULL)
                                count = xpathobj->nodesetval->nodeNr;

                        /*
                         * There are four possible cases, depending on the 
number of
                         * nodes returned by the XPath expression and the type 
of the
                         * target column: a) XPath returns no nodes.  b) One 
node is
                         * returned, and column is of type XML.  c) One node, 
column type
                         * other than XML.  d) Multiple nodes are returned.
                         */
                        if (xpathobj->nodesetval == NULL)
                        {
                                *isnull = true;
                        }
                        else if (count == 1 && targettypid == XMLOID)
                        {
                                textstr = 
xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[0],
                                                                                
           xtCxt->xmlerrcxt);
                                cstr = text_to_cstring(textstr);
                        }
                        else if (count == 1)
                        {
                                xmlChar    *str;

                                str = xmlNodeListGetString(xtCxt->doc,
                                                                                
   xpathobj->nodesetval->nodeTab[0]->xmlChildrenNode,
                                                                                
   1);
                                if (str)
                                {
                                        PG_TRY();
                                        {
                                                cstr = pstrdup(str);
                                        }
                                        PG_CATCH();
                                        {
                                                xmlFree(str);
                                                PG_RE_THROW();
                                        }
                                        PG_END_TRY();
                                        xmlFree(str);
                                }
                                else
                                        cstr = pstrdup("");
                        }
                        else
                        {
                                StringInfoData buf;
                                int                     i;

                                Assert(count > 1);

                                /*
                                 * When evaluating the XPath expression returns 
multiple
                                 * nodes, the result is the concatenation of 
them all.
                                 * The target type must be XML.
                                 */
                                if (targettypid != XMLOID)
                                        ereport(ERROR,
                                                        
(errcode(ERRCODE_CARDINALITY_VIOLATION),
                                                         errmsg("more than one 
value returned by column XPath expression")));

                                initStringInfo(&buf);
                                for (i = 0; i < count; i++)
                                        /* worth freeing the text here?  Naahh 
... */
                                        appendStringInfoText(&buf,
                                                                                
 xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
                                                                                
                                          xtCxt->xmlerrcxt));
                                cstr = buf.data;
                        }
                }
                else if (xpathobj->type == XPATH_STRING)
                {
                        cstr = (char *) xpathobj->stringval;
                        *isnull = false;
                }
                else
                        elog(ERROR, "unexpected XPath object type %u", 
xpathobj->type);

                /*
                 * By here, either cstr contains the result value, or the 
isnull flag
                 * has been set.
                 */
                Assert(cstr || *isnull);

                if (!*isnull)
                        result = InputFunctionCall(&state->in_functions[colnum],
                                                                           cstr,
                                                                           
state->typioparams[colnum],
                                                                           
attr->atttypmod);
        }
        PG_CATCH();
        {
                if (xpathobj != NULL)
                        xmlXPathFreeObject(xpathobj);
                PG_RE_THROW();
        }
        PG_END_TRY();

        if (xpathobj)
                xmlXPathFreeObject(xpathobj);

        return result;
#else
        NO_XML_SUPPORT();
#endif   /* not USE_LIBXML */
}

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to