pg_xpath_returnvalue.v1.patch
application/octet-stream
Filename: pg_xpath_returnvalue.v1.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v1
| File | + | − |
|---|---|---|
| src/backend/utils/adt/xml.c | 0 | 0 |
| src/test/regress/expected/xml.out | 0 | 0 |
| src/test/regress/sql/xml.sql | 0 | 0 |
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index eaf5b4d..682fb73 100644
*** a/src/backend/utils/adt/xml.c
--- b/src/backend/utils/adt/xml.c
*************** static bool print_xml_decl(StringInfo bu
*** 109,114 ****
--- 109,115 ----
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
bool preserve_whitespace, int encoding);
static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
+ static Datum xml_xpathobjtoxmlarray(xmlXPathObjectPtr obj);
#endif /* USE_LIBXML */
static StringInfo query_to_xml_internal(const char *query, char *tablename,
*************** xml_xmlnodetoxmltype(xmlNodePtr cur)
*** 3291,3296 ****
--- 3292,3384 ----
return result;
}
+
+ /*
+ * Convert XML XPath object (the result of evaluating a XPath expression)
+ * to an array of xml values. Nodesets are converted to an array containg
+ * the node's textual representation. Primitive values (float, double, string)
+ * are converted to a single-element array containg the value's string
+ * representation.
+ */
+ static Datum
+ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj)
+ {
+ int i;
+ char *str;
+ Datum datum;
+ Datum single_elem;
+ ArrayBuildState *astate = NULL;
+
+ switch (xpathobj->type) {
+ case XPATH_NODESET:
+ /* Accumulate the node's textual representations.
+ * We trust xml_xmlnodetoxmltype() to returns only well-formed
+ * xml fragments.
+ */
+ if (xpathobj->nodesetval != NULL)
+ {
+ for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
+ {
+ datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
+ astate = accumArrayResult(astate, datum,
+ false, XMLOID,
+ CurrentMemoryContext);
+ }
+ }
+ break;
+
+ case XPATH_BOOLEAN:
+ /* Convert the boolean result to an XML fragmenent containing
+ * only its textual representation. We trust boolout to return
+ * only strings which are valid XML.
+ */
+ datum = BoolGetDatum(xpathobj->boolval);
+ str = DatumGetCString(DirectFunctionCall1(boolout,
+ datum));
+ single_elem = PointerGetDatum(cstring_to_text(str));
+ goto single;
+
+ case XPATH_NUMBER:
+ /* Convert the numeric result to an XML fragmenent containing
+ * only its textual representation. We trust float8out to return
+ * only strings which are valid XML.
+ */
+ datum = Float8GetDatum(xpathobj->floatval);
+ str = DatumGetCString(DirectFunctionCall1(float8out,
+ datum));
+ single_elem = PointerGetDatum(cstring_to_text(str));
+ goto single;
+
+ case XPATH_STRING:
+ /* Convert the string result to an XML fragment. Since XPath
+ * expressions can produce arbitrary strings, we must verify
+ * the the string is a well-formed XML fragment.
+ */
+ datum = CStringGetDatum((char *) xpathobj->stringval);
+ single_elem = DirectFunctionCall1(xml_in, datum);
+ goto single;
+
+ single:
+ /* Create single-element array containing the result.
+ * Used in the non-nodeset cases.
+ */
+ astate = accumArrayResult(astate, single_elem,
+ false, XMLOID,
+ CurrentMemoryContext);
+ break;
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("xpath expression result type %d is unsupported",
+ xpathobj->type)));
+ }
+
+ if (astate == NULL)
+ return PointerGetDatum(construct_empty_array(XMLOID));
+ else
+ return makeArrayResult(astate, CurrentMemoryContext);
+ }
#endif
*************** xpath(PG_FUNCTION_ARGS)
*** 3312,3330 ****
text *xpath_expr_text = PG_GETARG_TEXT_P(0);
xmltype *data = PG_GETARG_XML_P(1);
ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
- ArrayBuildState *astate = NULL;
xmlParserCtxtPtr ctxt = NULL;
xmlDocPtr doc = NULL;
xmlXPathContextPtr xpathctx = NULL;
xmlXPathCompExprPtr xpathcomp = NULL;
xmlXPathObjectPtr xpathobj = NULL;
char *datastr;
int32 len;
int32 xpath_len;
xmlChar *string;
xmlChar *xpath_expr;
int i;
- int res_nitems;
int ndim;
Datum *ns_names_uris;
bool *ns_names_uris_nulls;
--- 3400,3417 ----
text *xpath_expr_text = PG_GETARG_TEXT_P(0);
xmltype *data = PG_GETARG_XML_P(1);
ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
xmlParserCtxtPtr ctxt = NULL;
xmlDocPtr doc = NULL;
xmlXPathContextPtr xpathctx = NULL;
xmlXPathCompExprPtr xpathcomp = NULL;
xmlXPathObjectPtr xpathobj = NULL;
+ Datum result = 0;
char *datastr;
int32 len;
int32 xpath_len;
xmlChar *string;
xmlChar *xpath_expr;
int i;
int ndim;
Datum *ns_names_uris;
bool *ns_names_uris_nulls;
*************** xpath(PG_FUNCTION_ARGS)
*** 3443,3468 ****
if (xpathobj == NULL) /* TODO: reason? */
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"could not create XPath object");
!
! /* return empty array in cases when nothing is found */
! if (xpathobj->nodesetval == NULL)
! res_nitems = 0;
! else
! res_nitems = xpathobj->nodesetval->nodeNr;
!
! if (res_nitems)
! {
! for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
! {
! Datum elem;
! bool elemisnull = false;
!
! elem = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
! astate = accumArrayResult(astate, elem,
! elemisnull, XMLOID,
! CurrentMemoryContext);
! }
! }
}
PG_CATCH();
{
--- 3530,3537 ----
if (xpathobj == NULL) /* TODO: reason? */
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"could not create XPath object");
!
! result = xml_xpathobjtoxmlarray(xpathobj);
}
PG_CATCH();
{
*************** xpath(PG_FUNCTION_ARGS)
*** 3485,3495 ****
xmlXPathFreeContext(xpathctx);
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
!
! if (res_nitems == 0)
! PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
! else
! PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
#else
NO_XML_SUPPORT();
return 0;
--- 3554,3561 ----
xmlXPathFreeContext(xpathctx);
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
!
! PG_RETURN_DATUM(result);
#else
NO_XML_SUPPORT();
return 0;
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index ecca589..78430f6 100644
*** a/src/test/regress/expected/xml.out
--- b/src/test/regress/expected/xml.out
*************** SELECT xpath('//b', '<a>one <b>two</b> t
*** 502,504 ****
--- 502,546 ----
{<b>two</b>,<b>etc</b>}
(1 row)
+ SELECT xpath('''<<invalid>>''', '<root/>');
+ ERROR: invalid XML content
+ DETAIL: Entity: line 1: parser error : StartTag: invalid element name
+ <<invalid>>
+ ^
+ Entity: line 1: parser error : Premature end of data in tag invalid line 1
+ <<invalid>>
+ ^
+ Entity: line 1: parser error : chunk is not well balanced
+ <<invalid>>
+ ^
+ CONTEXT: SQL function "xpath" statement 1
+ SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+ xpath
+ -------
+ {3}
+ (1 row)
+
+ SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+ xpath
+ -------
+ {f}
+ (1 row)
+
+ SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+ xpath
+ -------
+ {t}
+ (1 row)
+
+ SELECT xpath('name(/*)', '<root><sub/><sub/></root>');
+ xpath
+ --------
+ {root}
+ (1 row)
+
+ SELECT xpath('/nosuchtag', '<root/>');
+ xpath
+ -------
+ {}
+ (1 row)
+
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 086eedd..4af879d 100644
*** a/src/test/regress/sql/xml.sql
--- b/src/test/regress/sql/xml.sql
*************** SELECT xpath('', '<!-- error -->');
*** 163,165 ****
--- 163,171 ----
SELECT xpath('//text()', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>');
SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
+ SELECT xpath('''<<invalid>>''', '<root/>');
+ SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+ SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+ SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+ SELECT xpath('name(/*)', '<root><sub/><sub/></root>');
+ SELECT xpath('/nosuchtag', '<root/>');