@@ -6155,3 +6155,115 @@ openxml_simple(PG_FUNCTION_ARGS)
61556155#endif /* USE_LIBXML */
61566156}
61576157
6158+
6159+ PG_FUNCTION_INFO_V1 (bbf_xmlquery );
6160+
6161+ /*
6162+ * bbf_xmlquery - C implementation of XML .query() method
6163+ *
6164+ * Signature:
6165+ * sys.bbf_xmlquery(xpath_pattern TEXT, xml_element ANYELEMENT)
6166+ *
6167+ * Returns XML result of evaluating the XPath expression against the input.
6168+ * Returns empty XML if no nodes match.
6169+ *
6170+ * Validates:
6171+ * - Input must be XML type (or UDT based on XML)
6172+ * - QUOTED_IDENTIFIER must be ON
6173+ */
6174+ Datum
6175+ bbf_xmlquery (PG_FUNCTION_ARGS )
6176+ {
6177+ text * xpath_expr ;
6178+ Datum xml_datum ;
6179+ Oid arg_type ;
6180+ Oid immediate_base_type ;
6181+ ArrayType * namespaces ;
6182+ Datum xpath_result ;
6183+ ArrayType * result_arr ;
6184+ Datum * elems ;
6185+ bool * nulls ;
6186+ int nitems ;
6187+ StringInfoData buf ;
6188+ int i ;
6189+
6190+ xpath_expr = PG_GETARG_TEXT_PP (0 );
6191+ xml_datum = PG_GETARG_DATUM (1 );
6192+
6193+ /* Lookup the datatype of the supplied argument */
6194+ arg_type = get_fn_expr_argtype (fcinfo -> flinfo , 1 );
6195+
6196+ /* UDT handling: resolve to immediate base type if it's a UDT */
6197+ immediate_base_type = get_immediate_base_type_of_UDT_internal (arg_type );
6198+ if (OidIsValid (immediate_base_type ))
6199+ arg_type = immediate_base_type ;
6200+
6201+ if (arg_type != XMLOID )
6202+ {
6203+ const char * typname = NULL ;
6204+
6205+ /* Get T-SQL type name for error message */
6206+ if (common_utility_plugin_ptr )
6207+ typname = (* common_utility_plugin_ptr -> resolve_pg_type_to_tsql )(arg_type );
6208+ if (typname == NULL )
6209+ typname = format_type_be (arg_type );
6210+
6211+ ereport (ERROR ,
6212+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
6213+ errmsg ("Cannot call methods on %s." , typname )));
6214+ }
6215+
6216+ /* Check QUOTED_IDENTIFIER setting (required for XML methods in T-SQL) */
6217+ if (!pltsql_quoted_identifier )
6218+ ereport (ERROR ,
6219+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
6220+ errmsg ("SELECT failed because the following SET options have "
6221+ "incorrect settings: 'QUOTED_IDENTIFIER'. Verify that "
6222+ "SET options are correct for XML data type methods." )));
6223+
6224+ /*
6225+ * Call the built-in xpath(text, xml, text[][]) directly with an empty
6226+ * namespace array. Returns xml[] (array of XML fragments).
6227+ *
6228+ * TODO: when WITH XMLNAMESPACES is supported, populate this array with
6229+ * the declared (prefix, uri) pairs from the active namespace context.
6230+ */
6231+ namespaces = construct_empty_array (TEXTOID );
6232+ xpath_result = DirectFunctionCall3 (xpath ,
6233+ PointerGetDatum (xpath_expr ),
6234+ xml_datum ,
6235+ PointerGetDatum (namespaces ));
6236+
6237+ result_arr = DatumGetArrayTypeP (xpath_result );
6238+
6239+ /* Deconstruct the result array */
6240+ deconstruct_array (result_arr , XMLOID , -1 , false, TYPALIGN_INT ,
6241+ & elems , & nulls , & nitems );
6242+
6243+ /* Empty result → return empty string as XML (matches T-SQL behavior) */
6244+ if (nitems == 0 )
6245+ PG_RETURN_XML_P ((xmltype * ) cstring_to_text ("" ));
6246+
6247+ /* Single result → return directly (common fast path) */
6248+ if (nitems == 1 && !nulls [0 ])
6249+ PG_RETURN_DATUM (elems [0 ]);
6250+
6251+ /*
6252+ * Multiple results - concatenate all XML fragments.
6253+ * Equivalent to: SELECT xmlagg(x) FROM unnest(result_set) AS x
6254+ */
6255+ initStringInfo (& buf );
6256+ for (i = 0 ; i < nitems ; i ++ )
6257+ {
6258+ if (!nulls [i ])
6259+ {
6260+ text * fragment = DatumGetTextPP (elems [i ]);
6261+
6262+ appendBinaryStringInfo (& buf ,
6263+ VARDATA_ANY (fragment ),
6264+ VARSIZE_ANY_EXHDR (fragment ));
6265+ }
6266+ }
6267+
6268+ PG_RETURN_XML_P ((xmltype * ) cstring_to_text_with_len (buf .data , buf .len ));
6269+ }
0 commit comments