@@ -126,6 +126,8 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
126126static xmlDocPtr xml_parse (text * data , XmlOptionType xmloption_arg ,
127127 bool preserve_whitespace , int encoding );
128128static text * xml_xmlnodetoxmltype (xmlNodePtr cur );
129+ static int xml_xpathobjtoxmlarray (xmlXPathObjectPtr xpathobj ,
130+ ArrayBuildState * * astate );
129131#endif /* USE_LIBXML */
130132
131133static 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,
35033505 */
35043506
35053507#ifdef USE_LIBXML
3508+
35063509/*
35073510 * Convert XML node to text (dump subtree in case of element,
35083511 * return value otherwise)
@@ -3554,20 +3557,100 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
35543557
35553558 return result ;
35563559}
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+ }
35583641
35593642
35603643/*
35613644 * Common code for xpath() and xmlexists()
35623645 *
35633646 * 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.
35653649 *
35663650 * It is up to the user to ensure that the XML passed is in fact
35673651 * an XML document - XPath doesn't work easily on fragments without
35683652 * a context node being known.
35693653 */
3570- #ifdef USE_LIBXML
35713654static void
35723655xpath_internal (text * xpath_expr_text , xmltype * data , ArrayType * namespaces ,
35733656 int * res_nitems , ArrayBuildState * * astate )
@@ -3711,26 +3794,13 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
37113794 xml_ereport (xmlerrcxt , ERROR , ERRCODE_INTERNAL_ERROR ,
37123795 "could not create XPath object" );
37133796
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 );
37173802 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 );
37343804 }
37353805 PG_CATCH ();
37363806 {
0 commit comments