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