2008-05-17 03:28:26 +02:00
|
|
|
/*
|
2010-09-20 22:08:53 +02:00
|
|
|
* contrib/xml2/xpath.c
|
2008-05-17 03:28:26 +02:00
|
|
|
*
|
|
|
|
* Parser interface for DOM-based parser (libxml) rather than
|
2009-01-07 14:44:37 +01:00
|
|
|
* stream-based SAX-type parser
|
|
|
|
*/
|
2004-03-05 04:24:50 +01:00
|
|
|
#include "postgres.h"
|
2009-01-07 14:44:37 +01:00
|
|
|
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2004-03-05 04:24:50 +01:00
|
|
|
#include "executor/spi.h"
|
2009-01-07 14:44:37 +01:00
|
|
|
#include "fmgr.h"
|
2004-03-05 04:24:50 +01:00
|
|
|
#include "funcapi.h"
|
|
|
|
#include "lib/stringinfo.h"
|
2009-01-07 14:44:37 +01:00
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "utils/builtins.h"
|
2010-03-03 20:10:22 +01:00
|
|
|
#include "utils/xml.h"
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
/* libxml includes */
|
|
|
|
|
|
|
|
#include <libxml/xpath.h>
|
|
|
|
#include <libxml/tree.h>
|
|
|
|
#include <libxml/xmlmemory.h>
|
|
|
|
#include <libxml/xmlerror.h>
|
|
|
|
#include <libxml/parserInternals.h>
|
|
|
|
|
2006-09-11 17:30:32 +02:00
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
|
2010-03-03 20:10:22 +01:00
|
|
|
/* exported for use by xslt_proc.c */
|
2010-02-28 20:51:37 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness);
|
2010-02-28 20:51:37 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
/* workspace for pgxml_xpath() */
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
xmlDocPtr doctree;
|
|
|
|
xmlXPathContextPtr ctxt;
|
|
|
|
xmlXPathObjectPtr res;
|
|
|
|
} xpath_workspace;
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/* local declarations */
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
|
|
|
|
xmlChar *toptagname, xmlChar *septagname,
|
|
|
|
xmlChar *plainsep);
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlChar *septag, xmlChar *plainsep);
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
static xmlChar *pgxml_texttoxmlchar(text *textstring);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath,
|
|
|
|
xpath_workspace *workspace);
|
|
|
|
|
|
|
|
static void cleanup_workspace(xpath_workspace *workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
|
|
|
* Initialize for xml parsing.
|
2011-07-20 19:03:12 +02:00
|
|
|
*
|
|
|
|
* As with the underlying pg_xml_init function, calls to this MUST be followed
|
|
|
|
* by a PG_TRY block that guarantees that pg_xml_done is called.
|
2010-02-28 20:51:37 +01:00
|
|
|
*/
|
2011-07-20 19:03:12 +02:00
|
|
|
PgXmlErrorContext *
|
|
|
|
pgxml_parser_init(PgXmlStrictness strictness)
|
2004-03-05 04:24:50 +01:00
|
|
|
{
|
2011-07-20 19:03:12 +02:00
|
|
|
PgXmlErrorContext *xmlerrcxt;
|
|
|
|
|
2010-03-03 20:10:22 +01:00
|
|
|
/* Set up error handling (we share the core's error handler) */
|
2011-07-20 19:03:12 +02:00
|
|
|
xmlerrcxt = pg_xml_init(strictness);
|
|
|
|
|
|
|
|
/* Note: we're assuming an elog cannot be thrown by the following calls */
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/* Initialize libxml */
|
|
|
|
xmlInitParser();
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
xmlSubstituteEntitiesDefault(1);
|
2011-07-20 19:03:12 +02:00
|
|
|
|
|
|
|
return xmlerrcxt;
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-02 23:21:12 +01:00
|
|
|
/* Encodes special characters (<, >, &, " and \r) as XML entities */
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(xml_encode_special_chars);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xml_encode_special_chars(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tin = PG_GETARG_TEXT_PP(0);
|
2004-12-02 23:21:12 +01:00
|
|
|
text *tout;
|
|
|
|
xmlChar *ts,
|
|
|
|
*tt;
|
|
|
|
|
|
|
|
ts = pgxml_texttoxmlchar(tin);
|
|
|
|
|
|
|
|
tt = xmlEncodeSpecialChars(NULL, ts);
|
|
|
|
|
|
|
|
pfree(ts);
|
|
|
|
|
2008-05-04 18:42:41 +02:00
|
|
|
tout = cstring_to_text((char *) tt);
|
2004-12-02 23:21:12 +01:00
|
|
|
|
|
|
|
xmlFree(tt);
|
|
|
|
|
|
|
|
PG_RETURN_TEXT_P(tout);
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
|
|
|
* Function translates a nodeset into a text representation
|
|
|
|
*
|
|
|
|
* iterates over each node in the set and calls xmlNodeDump to write it to
|
|
|
|
* an xmlBuffer -from which an xmlChar * string is returned.
|
|
|
|
*
|
|
|
|
* each representation is surrounded by <tagname> ... </tagname>
|
|
|
|
*
|
|
|
|
* plainsep is an ordinary (not tag) separator - if used, then nodes are
|
|
|
|
* cast to string as output method
|
|
|
|
*/
|
|
|
|
static xmlChar *
|
2004-03-05 04:24:50 +01:00
|
|
|
pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
|
|
|
|
xmlChar *toptagname,
|
|
|
|
xmlChar *septagname,
|
|
|
|
xmlChar *plainsep)
|
|
|
|
{
|
|
|
|
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)
|
2007-07-16 01:30:19 +02:00
|
|
|
xmlBufferWriteChar(buf, (char *) plainsep);
|
2004-08-29 07:07:03 +02:00
|
|
|
}
|
2004-03-05 04:24:50 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
|
2004-08-29 07:07:03 +02:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlBufferWriteChar(buf, "<");
|
|
|
|
xmlBufferWriteCHAR(buf, septagname);
|
|
|
|
xmlBufferWriteChar(buf, ">");
|
2004-08-29 07:07:03 +02:00
|
|
|
}
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlNodeDump(buf,
|
|
|
|
nodeset->nodeTab[i]->doc,
|
|
|
|
nodeset->nodeTab[i],
|
2004-08-29 07:07:03 +02:00
|
|
|
1, 0);
|
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
|
2004-08-29 07:07:03 +02:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlBufferWriteChar(buf, "</");
|
|
|
|
xmlBufferWriteCHAR(buf, septagname);
|
|
|
|
xmlBufferWriteChar(buf, ">");
|
2004-08-29 07:07:03 +02:00
|
|
|
}
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
|
|
|
|
{
|
|
|
|
xmlBufferWriteChar(buf, "</");
|
|
|
|
xmlBufferWriteCHAR(buf, toptagname);
|
|
|
|
xmlBufferWriteChar(buf, ">");
|
|
|
|
}
|
|
|
|
result = xmlStrdup(buf->content);
|
|
|
|
xmlBufferFree(buf);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
|
|
|
|
* into the libxml2 representation
|
|
|
|
*/
|
2010-02-28 20:51:37 +01:00
|
|
|
static xmlChar *
|
2004-03-05 04:24:50 +01:00
|
|
|
pgxml_texttoxmlchar(text *textstring)
|
|
|
|
{
|
2008-05-04 18:42:41 +02:00
|
|
|
return (xmlChar *) text_to_cstring(textstring);
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/* Publicly visible XPath functions */
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
|
|
|
* This is a "raw" xpath function. Check that it returns child elements
|
2004-03-05 04:24:50 +01:00
|
|
|
* properly
|
|
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(xpath_nodeset);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xpath_nodeset(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *document = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
|
|
|
|
xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
|
|
|
|
xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(3));
|
2010-11-26 21:20:51 +01:00
|
|
|
xmlChar *xpath;
|
|
|
|
text *xpres;
|
|
|
|
xmlXPathObjectPtr res;
|
|
|
|
xpath_workspace workspace;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
xpath = pgxml_texttoxmlchar(xpathsupp);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
res = pgxml_xpath(document, xpath, &workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
xpres = pgxml_result_to_text(res, toptag, septag, NULL);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
cleanup_workspace(&workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2004-10-13 03:26:42 +02:00
|
|
|
pfree(xpath);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
if (xpres == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_TEXT_P(xpres);
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
|
|
|
* The following function is almost identical, but returns the elements in
|
|
|
|
* a list.
|
|
|
|
*/
|
2004-03-05 04:24:50 +01:00
|
|
|
PG_FUNCTION_INFO_V1(xpath_list);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xpath_list(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *document = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
|
|
|
|
xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2));
|
2010-11-26 21:20:51 +01:00
|
|
|
xmlChar *xpath;
|
|
|
|
text *xpres;
|
|
|
|
xmlXPathObjectPtr res;
|
|
|
|
xpath_workspace workspace;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
xpath = pgxml_texttoxmlchar(xpathsupp);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
res = pgxml_xpath(document, xpath, &workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
xpres = pgxml_result_to_text(res, NULL, NULL, plainsep);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
cleanup_workspace(&workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2004-10-13 03:26:42 +02:00
|
|
|
pfree(xpath);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
if (xpres == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_TEXT_P(xpres);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(xpath_string);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xpath_string(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *document = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlChar *xpath;
|
|
|
|
int32 pathsize;
|
2010-11-26 21:20:51 +01:00
|
|
|
text *xpres;
|
|
|
|
xmlXPathObjectPtr res;
|
|
|
|
xpath_workspace workspace;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2017-03-13 00:35:34 +01:00
|
|
|
pathsize = VARSIZE_ANY_EXHDR(xpathsupp);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
Replace a bunch more uses of strncpy() with safer coding.
strncpy() has a well-deserved reputation for being unsafe, so make an
effort to get rid of nearly all occurrences in HEAD.
A large fraction of the remaining uses were passing length less than or
equal to the known strlen() of the source, in which case no null-padding
can occur and the behavior is equivalent to memcpy(), though doubtless
slower and certainly harder to reason about. So just use memcpy() in
these cases.
In other cases, use either StrNCpy() or strlcpy() as appropriate (depending
on whether padding to the full length of the destination buffer seems
useful).
I left a few strncpy() calls alone in the src/timezone/ code, to keep it
in sync with upstream (the IANA tzcode distribution). There are also a
few such calls in ecpg that could possibly do with more analysis.
AFAICT, none of these changes are more than cosmetic, except for the four
occurrences in fe-secure-openssl.c, which are in fact buggy: an overlength
source leads to a non-null-terminated destination buffer and ensuing
misbehavior. These don't seem like security issues, first because no stack
clobber is possible and second because if your values of sslcert etc are
coming from untrusted sources then you've got problems way worse than this.
Still, it's undesirable to have unpredictable behavior for overlength
inputs, so back-patch those four changes to all active branches.
2015-01-24 19:05:42 +01:00
|
|
|
memcpy((char *) xpath, "string(", 7);
|
2017-03-13 00:35:34 +01:00
|
|
|
memcpy((char *) (xpath + 7), VARDATA_ANY(xpathsupp), pathsize);
|
2004-03-05 04:24:50 +01:00
|
|
|
xpath[pathsize + 7] = ')';
|
|
|
|
xpath[pathsize + 8] = '\0';
|
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
res = pgxml_xpath(document, xpath, &workspace);
|
|
|
|
|
|
|
|
xpres = pgxml_result_to_text(res, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
cleanup_workspace(&workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2004-10-13 03:26:42 +02:00
|
|
|
pfree(xpath);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
if (xpres == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_TEXT_P(xpres);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(xpath_number);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xpath_number(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *document = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlChar *xpath;
|
|
|
|
float4 fRes;
|
|
|
|
xmlXPathObjectPtr res;
|
2010-11-26 21:20:51 +01:00
|
|
|
xpath_workspace workspace;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
xpath = pgxml_texttoxmlchar(xpathsupp);
|
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
res = pgxml_xpath(document, xpath, &workspace);
|
|
|
|
|
2004-10-13 03:26:42 +02:00
|
|
|
pfree(xpath);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
if (res == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
fRes = xmlXPathCastToNumber(res);
|
2010-03-01 06:16:35 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
cleanup_workspace(&workspace);
|
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
if (xmlXPathIsNaN(fRes))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
PG_RETURN_FLOAT4(fRes);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(xpath_bool);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xpath_bool(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *document = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlChar *xpath;
|
|
|
|
int bRes;
|
|
|
|
xmlXPathObjectPtr res;
|
2010-11-26 21:20:51 +01:00
|
|
|
xpath_workspace workspace;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
xpath = pgxml_texttoxmlchar(xpathsupp);
|
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
res = pgxml_xpath(document, xpath, &workspace);
|
|
|
|
|
2004-10-13 03:26:42 +02:00
|
|
|
pfree(xpath);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
if (res == NULL)
|
|
|
|
PG_RETURN_BOOL(false);
|
|
|
|
|
|
|
|
bRes = xmlXPathCastToBoolean(res);
|
2010-03-01 06:16:35 +01:00
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
cleanup_workspace(&workspace);
|
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
PG_RETURN_BOOL(bRes);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Core function to evaluate XPath query */
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
static xmlXPathObjectPtr
|
2010-11-26 21:20:51 +01:00
|
|
|
pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
|
2004-03-05 04:24:50 +01:00
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
int32 docsize = VARSIZE_ANY_EXHDR(document);
|
2011-07-20 19:03:12 +02:00
|
|
|
PgXmlErrorContext *xmlerrcxt;
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlXPathCompExprPtr comppath;
|
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
workspace->doctree = NULL;
|
|
|
|
workspace->ctxt = NULL;
|
|
|
|
workspace->res = NULL;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
PG_TRY();
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
workspace->doctree = xmlParseMemory((char *) VARDATA_ANY(document),
|
2011-07-20 19:03:12 +02:00
|
|
|
docsize);
|
|
|
|
if (workspace->doctree != NULL)
|
|
|
|
{
|
|
|
|
workspace->ctxt = xmlXPathNewContext(workspace->doctree);
|
|
|
|
workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
/* compile the path */
|
|
|
|
comppath = xmlXPathCompile(xpath);
|
|
|
|
if (comppath == NULL)
|
|
|
|
xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
|
|
|
|
"XPath Syntax Error");
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
/* Now evaluate the path expression. */
|
|
|
|
workspace->res = xmlXPathCompiledEval(comppath, workspace->ctxt);
|
|
|
|
|
|
|
|
xmlXPathFreeCompExpr(comppath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PG_CATCH();
|
2004-03-05 04:24:50 +01:00
|
|
|
{
|
2010-11-26 21:20:51 +01:00
|
|
|
cleanup_workspace(workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
pg_xml_done(xmlerrcxt, true);
|
2010-11-26 21:20:51 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
PG_RE_THROW();
|
|
|
|
}
|
|
|
|
PG_END_TRY();
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
if (workspace->res == NULL)
|
2010-11-26 21:20:51 +01:00
|
|
|
cleanup_workspace(workspace);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
pg_xml_done(xmlerrcxt, false);
|
|
|
|
|
|
|
|
return workspace->res;
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
|
2010-11-26 21:20:51 +01:00
|
|
|
/* Clean up after processing the result of pgxml_xpath() */
|
|
|
|
static void
|
|
|
|
cleanup_workspace(xpath_workspace *workspace)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
static text *
|
2004-03-05 04:24:50 +01:00
|
|
|
pgxml_result_to_text(xmlXPathObjectPtr res,
|
|
|
|
xmlChar *toptag,
|
|
|
|
xmlChar *septag,
|
|
|
|
xmlChar *plainsep)
|
|
|
|
{
|
|
|
|
xmlChar *xpresstr;
|
|
|
|
text *xpres;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
if (res == NULL)
|
|
|
|
return NULL;
|
2010-03-01 06:16:35 +01:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
switch (res->type)
|
|
|
|
{
|
|
|
|
case XPATH_NODESET:
|
|
|
|
xpresstr = pgxmlNodeSetToText(res->nodesetval,
|
|
|
|
toptag,
|
|
|
|
septag, plainsep);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case XPATH_STRING:
|
|
|
|
xpresstr = xmlStrdup(res->stringval);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2006-03-01 07:30:32 +01:00
|
|
|
elog(NOTICE, "unsupported XQuery result: %d", res->type);
|
2007-07-16 01:30:19 +02:00
|
|
|
xpresstr = xmlStrdup((const xmlChar *) "<unsupported/>");
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now convert this result back to text */
|
2008-05-04 18:42:41 +02:00
|
|
|
xpres = cstring_to_text((char *) xpresstr);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
/* Free various storage */
|
|
|
|
xmlFree(xpresstr);
|
|
|
|
|
|
|
|
return xpres;
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
|
|
|
* xpath_table is a table function. It needs some tidying (as do the
|
2004-03-05 04:24:50 +01:00
|
|
|
* other functions here!
|
|
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(xpath_table);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
xpath_table(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2010-02-28 20:51:37 +01:00
|
|
|
/* 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 */
|
2004-03-05 04:24:50 +01:00
|
|
|
SPITupleTable *tuptable;
|
|
|
|
HeapTuple spi_tuple;
|
|
|
|
TupleDesc spi_tupdesc;
|
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
|
|
|
AttInMetadata *attinmeta;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
char **values;
|
|
|
|
xmlChar **xpaths;
|
2007-07-16 01:30:19 +02:00
|
|
|
char *pos;
|
|
|
|
const char *pathsep = "|";
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
int numpaths;
|
|
|
|
int ret;
|
Widen query numbers-of-tuples-processed counters to uint64.
This patch widens SPI_processed, EState's es_processed field, PortalData's
portalPos field, FuncCallContext's call_cntr and max_calls fields,
ExecutorRun's count argument, PortalRunFetch's result, and the max number
of rows in a SPITupleTable to uint64, and deals with (I hope) all the
ensuing fallout. Some of these values were declared uint32 before, and
others "long".
I also removed PortalData's posOverflow field, since that logic seems
pretty useless given that portalPos is now always 64 bits.
The user-visible results are that command tags for SELECT etc will
correctly report tuple counts larger than 4G, as will plpgsql's GET
GET DIAGNOSTICS ... ROW_COUNT command. Queries processing more tuples
than that are still not exactly the norm, but they're becoming more
common.
Most values associated with FETCH/MOVE distances, such as PortalRun's count
argument and the count argument of most SPI functions that have one, remain
declared as "long". It's not clear whether it would be worth promoting
those to int64; but it would definitely be a large dollop of additional
API churn on top of this, and it would only help 32-bit platforms which
seem relatively less likely to see any benefit.
Andreas Scherbaum, reviewed by Christian Ullrich, additional hacking by me
2016-03-12 22:05:10 +01:00
|
|
|
uint64 proc;
|
2004-03-05 04:24:50 +01:00
|
|
|
int j;
|
|
|
|
int rownr; /* For issuing multiple rows from one original
|
|
|
|
* document */
|
2010-02-28 22:31:57 +01:00
|
|
|
bool had_values; /* To determine end of nodeset results */
|
2006-03-01 07:51:01 +01:00
|
|
|
StringInfoData query_buf;
|
2011-07-20 19:03:12 +02:00
|
|
|
PgXmlErrorContext *xmlerrcxt;
|
|
|
|
volatile xmlDocPtr doctree = NULL;
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2022-10-18 03:22:40 +02:00
|
|
|
InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-02-28 22:31:57 +01:00
|
|
|
/* must have at least one output column (for the pkey) */
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
if (rsinfo->setDesc->natts < 1)
|
2010-02-28 22:31:57 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("xpath_table must have at least one output column")));
|
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/*
|
|
|
|
* At the moment we assume that the returned attributes make sense for the
|
2017-02-06 10:33:58 +01:00
|
|
|
* XPath specified (i.e. we trust the caller). It's not fatal if they get
|
2004-03-05 04:24:50 +01:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
|
|
|
|
xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
|
2004-03-05 04:24:50 +01:00
|
|
|
|
2010-02-28 22:31:57 +01:00
|
|
|
/*
|
|
|
|
* Split XPaths. xpathset is a writable CString.
|
|
|
|
*
|
|
|
|
* Note that we stop splitting once we've done all needed for tupdesc
|
|
|
|
*/
|
2004-03-05 04:24:50 +01:00
|
|
|
numpaths = 0;
|
|
|
|
pos = xpathset;
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
while (numpaths < (rsinfo->setDesc->natts - 1))
|
2004-08-29 07:07:03 +02:00
|
|
|
{
|
2010-02-28 22:31:57 +01:00
|
|
|
xpaths[numpaths++] = (xmlChar *) pos;
|
2004-03-05 04:24:50 +01:00
|
|
|
pos = strstr(pos, pathsep);
|
|
|
|
if (pos != NULL)
|
2004-08-29 07:07:03 +02:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
*pos = '\0';
|
2004-08-29 07:07:03 +02:00
|
|
|
pos++;
|
|
|
|
}
|
2010-02-28 22:31:57 +01:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* Now build query */
|
2006-03-01 07:51:01 +01:00
|
|
|
initStringInfo(&query_buf);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
/* Build initial sql statement */
|
2006-03-01 07:51:01 +01:00
|
|
|
appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s",
|
2004-03-05 04:24:50 +01:00
|
|
|
pkeyfield,
|
|
|
|
xmlfield,
|
|
|
|
relname,
|
2010-02-28 20:51:37 +01:00
|
|
|
condition);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
if ((ret = SPI_connect()) < 0)
|
|
|
|
elog(ERROR, "xpath_table: SPI_connect returned %d", ret);
|
|
|
|
|
2006-03-01 07:51:01 +01:00
|
|
|
if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT)
|
2010-02-28 20:51:37 +01:00
|
|
|
elog(ERROR, "xpath_table: SPI execution failed for query %s",
|
|
|
|
query_buf.data);
|
2004-03-05 04:24:50 +01:00
|
|
|
|
|
|
|
proc = SPI_processed;
|
|
|
|
tuptable = SPI_tuptable;
|
|
|
|
spi_tupdesc = tuptable->tupdesc;
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2004-03-05 04:24:50 +01:00
|
|
|
if (spi_tupdesc->natts != 2)
|
|
|
|
{
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
2006-03-01 07:30:32 +01:00
|
|
|
errmsg("expression returning multiple columns is not valid in parameter list"),
|
|
|
|
errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts)));
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
|
2010-02-28 20:51:37 +01:00
|
|
|
/*
|
2010-03-03 20:10:22 +01:00
|
|
|
* Setup the parser. This should happen after we are done evaluating the
|
|
|
|
* query, in case it calls functions that set up libxml differently.
|
2010-02-28 20:51:37 +01:00
|
|
|
*/
|
2011-07-20 19:03:12 +02:00
|
|
|
xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
PG_TRY();
|
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
/* For each row i.e. document returned from SPI */
|
Widen query numbers-of-tuples-processed counters to uint64.
This patch widens SPI_processed, EState's es_processed field, PortalData's
portalPos field, FuncCallContext's call_cntr and max_calls fields,
ExecutorRun's count argument, PortalRunFetch's result, and the max number
of rows in a SPITupleTable to uint64, and deals with (I hope) all the
ensuing fallout. Some of these values were declared uint32 before, and
others "long".
I also removed PortalData's posOverflow field, since that logic seems
pretty useless given that portalPos is now always 64 bits.
The user-visible results are that command tags for SELECT etc will
correctly report tuple counts larger than 4G, as will plpgsql's GET
GET DIAGNOSTICS ... ROW_COUNT command. Queries processing more tuples
than that are still not exactly the norm, but they're becoming more
common.
Most values associated with FETCH/MOVE distances, such as PortalRun's count
argument and the count argument of most SPI functions that have one, remain
declared as "long". It's not clear whether it would be worth promoting
those to int64; but it would definitely be a large dollop of additional
API churn on top of this, and it would only help 32-bit platforms which
seem relatively less likely to see any benefit.
Andreas Scherbaum, reviewed by Christian Ullrich, additional hacking by me
2016-03-12 22:05:10 +01:00
|
|
|
uint64 i;
|
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
for (i = 0; i < proc; i++)
|
2008-03-26 02:19:04 +01:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
char *pkey;
|
|
|
|
char *xmldoc;
|
2008-03-26 02:19:04 +01:00
|
|
|
xmlXPathContextPtr ctxt;
|
2004-03-05 04:24:50 +01:00
|
|
|
xmlXPathObjectPtr res;
|
|
|
|
xmlChar *resstr;
|
|
|
|
xmlXPathCompExprPtr comppath;
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
HeapTuple ret_tuple;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* Extract the row data as C Strings */
|
2008-03-26 02:19:04 +01:00
|
|
|
spi_tuple = tuptable->vals[i];
|
2004-03-05 04:24:50 +01:00
|
|
|
pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
|
|
|
|
xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
|
2012-06-10 21:20:04 +02:00
|
|
|
|
|
|
|
/*
|
2008-03-26 02:19:04 +01:00
|
|
|
* Clear the values array, so that not-well-formed documents
|
2010-02-28 20:51:37 +01:00
|
|
|
* return NULL in all columns. Note that this also means that
|
|
|
|
* spare columns will be NULL.
|
2012-06-10 21:20:04 +02:00
|
|
|
*/
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
for (j = 0; j < rsinfo->setDesc->natts; j++)
|
2004-03-05 04:24:50 +01:00
|
|
|
values[j] = NULL;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* Insert primary key */
|
|
|
|
values[0] = pkey;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* Parse the document */
|
2008-03-26 02:19:04 +01:00
|
|
|
if (xmldoc)
|
|
|
|
doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
|
|
|
|
else /* treat NULL as not well-formed */
|
2004-03-05 04:24:50 +01:00
|
|
|
doctree = NULL;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
if (doctree == NULL)
|
|
|
|
{
|
|
|
|
/* not well-formed, so output all-NULL tuple */
|
|
|
|
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
tuplestore_puttuple(rsinfo->setResult, ret_tuple);
|
2004-03-05 04:24:50 +01:00
|
|
|
heap_freetuple(ret_tuple);
|
2012-06-10 21:20:04 +02:00
|
|
|
}
|
2004-03-05 04:24:50 +01:00
|
|
|
else
|
2012-06-10 21:20:04 +02:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
/* New loop here - we have to deal with nodeset results */
|
|
|
|
rownr = 0;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
|
|
|
do
|
2004-03-05 04:24:50 +01:00
|
|
|
{
|
|
|
|
/* Now evaluate the set of xpaths. */
|
2010-02-28 22:31:57 +01:00
|
|
|
had_values = false;
|
2004-03-05 04:24:50 +01:00
|
|
|
for (j = 0; j < numpaths; j++)
|
2012-06-10 21:20:04 +02:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
ctxt = xmlXPathNewContext(doctree);
|
|
|
|
ctxt->node = xmlDocGetRootElement(doctree);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* compile the path */
|
|
|
|
comppath = xmlXPathCompile(xpaths[j]);
|
|
|
|
if (comppath == NULL)
|
2011-07-20 19:03:12 +02:00
|
|
|
xml_ereport(xmlerrcxt, ERROR,
|
|
|
|
ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
|
2010-03-03 20:10:22 +01:00
|
|
|
"XPath Syntax Error");
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* Now evaluate the path expression. */
|
|
|
|
res = xmlXPathCompiledEval(comppath, ctxt);
|
|
|
|
xmlXPathFreeCompExpr(comppath);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
if (res != NULL)
|
|
|
|
{
|
|
|
|
switch (res->type)
|
2012-06-10 21:20:04 +02:00
|
|
|
{
|
2004-03-05 04:24:50 +01:00
|
|
|
case XPATH_NODESET:
|
|
|
|
/* We see if this nodeset has enough nodes */
|
2010-02-28 22:31:57 +01:00
|
|
|
if (res->nodesetval != NULL &&
|
|
|
|
rownr < res->nodesetval->nodeNr)
|
2004-03-05 04:24:50 +01:00
|
|
|
{
|
2011-07-20 19:03:12 +02:00
|
|
|
resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
|
2010-02-28 22:31:57 +01:00
|
|
|
had_values = true;
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
resstr = NULL;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
break;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
case XPATH_STRING:
|
|
|
|
resstr = xmlStrdup(res->stringval);
|
|
|
|
break;
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
default:
|
2006-03-01 07:30:32 +01:00
|
|
|
elog(NOTICE, "unsupported XQuery result: %d", res->type);
|
2007-07-16 01:30:19 +02:00
|
|
|
resstr = xmlStrdup((const xmlChar *) "<unsupported/>");
|
2012-06-10 21:20:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-03-05 04:24:50 +01:00
|
|
|
* Insert this into the appropriate column in the
|
|
|
|
* result tuple.
|
2012-06-10 21:20:04 +02:00
|
|
|
*/
|
2007-07-16 01:30:19 +02:00
|
|
|
values[j + 1] = (char *) resstr;
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
}
|
2010-02-28 20:51:37 +01:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/* Now add the tuple to the output, if there is one. */
|
|
|
|
if (had_values)
|
|
|
|
{
|
|
|
|
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
|
Simplify SRFs using materialize mode in contrib/ modules
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
2022-03-08 02:12:22 +01:00
|
|
|
tuplestore_puttuple(rsinfo->setResult, ret_tuple);
|
2004-03-05 04:24:50 +01:00
|
|
|
heap_freetuple(ret_tuple);
|
|
|
|
}
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
rownr++;
|
|
|
|
} while (had_values);
|
|
|
|
}
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2011-07-20 19:03:12 +02:00
|
|
|
if (doctree != NULL)
|
|
|
|
xmlFreeDoc(doctree);
|
|
|
|
doctree = NULL;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2008-03-26 02:19:04 +01:00
|
|
|
if (pkey)
|
|
|
|
pfree(pkey);
|
|
|
|
if (xmldoc)
|
|
|
|
pfree(xmldoc);
|
2004-03-05 04:24:50 +01:00
|
|
|
}
|
2011-07-20 19:03:12 +02:00
|
|
|
}
|
|
|
|
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);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
SPI_finish();
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-03-05 04:24:50 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|