@@ -126,6 +126,8 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
126
126
static xmlDocPtr xml_parse (text * data , XmlOptionType xmloption_arg ,
127
127
bool preserve_whitespace , int encoding );
128
128
static text * xml_xmlnodetoxmltype (xmlNodePtr cur );
129
+ static int xml_xpathobjtoxmlarray (xmlXPathObjectPtr xpathobj ,
130
+ ArrayBuildState * * astate );
129
131
#endif /* USE_LIBXML */
130
132
131
133
static StringInfo query_to_xml_internal (const char * query , char * tablename ,
@@ -3503,6 +3505,7 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
3503
3505
*/
3504
3506
3505
3507
#ifdef USE_LIBXML
3508
+
3506
3509
/*
3507
3510
* Convert XML node to text (dump subtree in case of element,
3508
3511
* return value otherwise)
@@ -3554,20 +3557,100 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
3554
3557
3555
3558
return result ;
3556
3559
}
3557
- #endif
3560
+
3561
+ /*
3562
+ * Convert an XML XPath object (the result of evaluating an XPath expression)
3563
+ * to an array of xml values, which is returned at *astate. The function
3564
+ * result value is the number of elements in the array.
3565
+ *
3566
+ * If "astate" is NULL then we don't generate the array value, but we still
3567
+ * return the number of elements it would have had.
3568
+ *
3569
+ * Nodesets are converted to an array containing the nodes' textual
3570
+ * representations. Primitive values (float, double, string) are converted
3571
+ * to a single-element array containing the value's string representation.
3572
+ */
3573
+ static int
3574
+ xml_xpathobjtoxmlarray (xmlXPathObjectPtr xpathobj ,
3575
+ ArrayBuildState * * astate )
3576
+ {
3577
+ int result = 0 ;
3578
+ Datum datum ;
3579
+ Oid datumtype ;
3580
+ char * result_str ;
3581
+
3582
+ if (astate != NULL )
3583
+ * astate = NULL ;
3584
+
3585
+ switch (xpathobj -> type )
3586
+ {
3587
+ case XPATH_NODESET :
3588
+ if (xpathobj -> nodesetval != NULL )
3589
+ {
3590
+ result = xpathobj -> nodesetval -> nodeNr ;
3591
+ if (astate != NULL )
3592
+ {
3593
+ int i ;
3594
+
3595
+ for (i = 0 ; i < result ; i ++ )
3596
+ {
3597
+ datum = PointerGetDatum (xml_xmlnodetoxmltype (xpathobj -> nodesetval -> nodeTab [i ]));
3598
+ * astate = accumArrayResult (* astate , datum ,
3599
+ false, XMLOID ,
3600
+ CurrentMemoryContext );
3601
+ }
3602
+ }
3603
+ }
3604
+ return result ;
3605
+
3606
+ case XPATH_BOOLEAN :
3607
+ if (astate == NULL )
3608
+ return 1 ;
3609
+ datum = BoolGetDatum (xpathobj -> boolval );
3610
+ datumtype = BOOLOID ;
3611
+ break ;
3612
+
3613
+ case XPATH_NUMBER :
3614
+ if (astate == NULL )
3615
+ return 1 ;
3616
+ datum = Float8GetDatum (xpathobj -> floatval );
3617
+ datumtype = FLOAT8OID ;
3618
+ break ;
3619
+
3620
+ case XPATH_STRING :
3621
+ if (astate == NULL )
3622
+ return 1 ;
3623
+ datum = CStringGetDatum ((char * ) xpathobj -> stringval );
3624
+ datumtype = CSTRINGOID ;
3625
+ break ;
3626
+
3627
+ default :
3628
+ elog (ERROR , "xpath expression result type %d is unsupported" ,
3629
+ xpathobj -> type );
3630
+ return 0 ; /* keep compiler quiet */
3631
+ }
3632
+
3633
+ /* Common code for scalar-value cases */
3634
+ result_str = map_sql_value_to_xml_value (datum , datumtype , true);
3635
+ datum = PointerGetDatum (cstring_to_xmltype (result_str ));
3636
+ * astate = accumArrayResult (* astate , datum ,
3637
+ false, XMLOID ,
3638
+ CurrentMemoryContext );
3639
+ return 1 ;
3640
+ }
3558
3641
3559
3642
3560
3643
/*
3561
3644
* Common code for xpath() and xmlexists()
3562
3645
*
3563
3646
* Evaluate XPath expression and return number of nodes in res_items
3564
- * and array of XML values in astate.
3647
+ * and array of XML values in astate. Either of those pointers can be
3648
+ * NULL if the corresponding result isn't wanted.
3565
3649
*
3566
3650
* It is up to the user to ensure that the XML passed is in fact
3567
3651
* an XML document - XPath doesn't work easily on fragments without
3568
3652
* a context node being known.
3569
3653
*/
3570
- #ifdef USE_LIBXML
3571
3654
static void
3572
3655
xpath_internal (text * xpath_expr_text , xmltype * data , ArrayType * namespaces ,
3573
3656
int * res_nitems , ArrayBuildState * * astate )
@@ -3711,26 +3794,13 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
3711
3794
xml_ereport (xmlerrcxt , ERROR , ERRCODE_INTERNAL_ERROR ,
3712
3795
"could not create XPath object" );
3713
3796
3714
- /* return empty array in cases when nothing is found */
3715
- if (xpathobj -> nodesetval == NULL )
3716
- * res_nitems = 0 ;
3797
+ /*
3798
+ * Extract the results as requested.
3799
+ */
3800
+ if (res_nitems != NULL )
3801
+ * res_nitems = xml_xpathobjtoxmlarray (xpathobj , astate );
3717
3802
else
3718
- * res_nitems = xpathobj -> nodesetval -> nodeNr ;
3719
-
3720
- if (* res_nitems && astate )
3721
- {
3722
- * astate = NULL ;
3723
- for (i = 0 ; i < xpathobj -> nodesetval -> nodeNr ; i ++ )
3724
- {
3725
- Datum elem ;
3726
- bool elemisnull = false;
3727
-
3728
- elem = PointerGetDatum (xml_xmlnodetoxmltype (xpathobj -> nodesetval -> nodeTab [i ]));
3729
- * astate = accumArrayResult (* astate , elem ,
3730
- elemisnull , XMLOID ,
3731
- CurrentMemoryContext );
3732
- }
3733
- }
3803
+ (void ) xml_xpathobjtoxmlarray (xpathobj , astate );
3734
3804
}
3735
3805
PG_CATCH ();
3736
3806
{
0 commit comments