Re: patch: function xmltable

From: Alvaro Herrera <alvherre(at)2ndquadrant(dot)com>
To: Pavel Stehule <pavel(dot)stehule(at)gmail(dot)com>
Cc: Craig Ringer <craig(at)2ndquadrant(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: patch: function xmltable
Date: 2017-01-16 23:30:00
Message-ID: 20170116233000.aooak7ytomrhvyjo@alvherre.pgsql
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

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

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message David Rowley 2017-01-16 23:42:34 Re: PoC: Grouped base relation
Previous Message Alvaro Herrera 2017-01-16 22:51:13 Re: patch: function xmltable