pg_xpath_returnvalue.v2.patch
application/octet-stream
Filename: pg_xpath_returnvalue.v2.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 v2
| 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 359cded..681bbee 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,3368 ----
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;
+ Datum datum;
+ char* result_str;
+ ArrayBuildState *result_astate = NULL;
+
+ switch (xpathobj->type) {
+ /* For node sets, we append all the node's textual representations
+ * to the array
+ */
+ case XPATH_NODESET:
+ if (xpathobj->nodesetval != NULL)
+ {
+ for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
+ {
+ datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
+ result_astate = accumArrayResult(result_astate, datum,
+ false, XMLOID,
+ CurrentMemoryContext);
+ }
+ }
+ break;
+
+ /* For scalar values, we encode the value in XML and return a
+ * single-element array
+ */
+
+ case XPATH_BOOLEAN:
+ datum = BoolGetDatum(xpathobj->boolval);
+ result_str = map_sql_value_to_xml_value(datum, BOOLOID, true);
+ goto single;
+
+ case XPATH_NUMBER:
+ datum = Float8GetDatum(xpathobj->boolval);
+ result_str = map_sql_value_to_xml_value(datum, FLOAT8OID, true);
+ goto single;
+
+ case XPATH_STRING:
+ datum = CStringGetDatum((char *) xpathobj->stringval);
+ result_str = map_sql_value_to_xml_value(datum, CSTRINGOID, true);
+ goto single;
+
+ single:
+ datum = PointerGetDatum(cstring_to_xmltype(result_str));
+ result_astate = accumArrayResult(result_astate, datum,
+ false, XMLOID,
+ CurrentMemoryContext);
+ break;
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("xpath expression result type %d is unsupported",
+ xpathobj->type)));
+ }
+
+ if (result_astate == NULL)
+ return PointerGetDatum(construct_empty_array(XMLOID));
+ else
+ return makeArrayResult(result_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;
--- 3384,3401 ----
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();
{
--- 3514,3521 ----
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;
--- 3538,3545 ----
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..6a588cb 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,540 ----
{<b>two</b>,<b>etc</b>}
(1 row)
+ SELECT xpath('''<<invalid>>''', '<root/>');
+ xpath
+ ---------------------------
+ {<<invalid>>}
+ (1 row)
+
+ SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+ xpath
+ -------
+ {0}
+ (1 row)
+
+ SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+ xpath
+ ---------
+ {false}
+ (1 row)
+
+ SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+ xpath
+ --------
+ {true}
+ (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/>');