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