@@ -148,6 +148,66 @@ async def sql(self, sql: str) -> List[Record]:
148148 rows = await od ._query_sql (sql )
149149 return [Record .from_api_response ("" , row ) for row in rows ]
150150
151+ # --------------------------------------------------------------- fetchxml
152+
153+ def fetchxml (self , xml : str ) -> AsyncFetchXmlQuery :
154+ """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object.
155+
156+ No HTTP request is made until
157+ :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute`
158+ or
159+ :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages`
160+ is called on the returned object.
161+
162+ :param xml: Well-formed FetchXML query string. The root ``<entity name="...">``
163+ element determines the entity set endpoint.
164+ :type xml: :class:`str`
165+ :return: Inert async query object.
166+ :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery`
167+ :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL
168+ length limit when encoded.
169+ :raises ValueError: If the FetchXML is missing a root ``<entity>`` element or name.
170+
171+ Example::
172+
173+ query = client.query.fetchxml(\" \" \"
174+ <fetch top="50">
175+ <entity name="account">
176+ <attribute name="name" />
177+ </entity>
178+ </fetch>
179+ \" \" \" )
180+
181+ # Eager — all pages collected:
182+ result = await query.execute()
183+ df = result.to_dataframe()
184+
185+ # Lazy — one page at a time:
186+ async for page in query.execute_pages():
187+ process(page.to_dataframe())
188+ """
189+ if not isinstance (xml , str ):
190+ raise ValidationError ("xml must be a string" )
191+ xml = xml .strip ()
192+ if not xml :
193+ raise ValidationError ("xml must not be empty" )
194+ if len (_url_quote (xml , safe = "" )) > _MAX_URL_LENGTH :
195+ raise ValidationError (
196+ f"FetchXML exceeds the Dataverse URL length limit ({ _MAX_URL_LENGTH :,} characters) when encoded. "
197+ "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB."
198+ )
199+ try :
200+ root_el = _ET .fromstring (xml )
201+ except _ET .ParseError as exc :
202+ raise ValidationError (f"xml is not well-formed: { exc } " ) from exc
203+ entity_el = root_el .find ("entity" )
204+ if entity_el is None :
205+ raise ValueError ("FetchXML must contain an <entity> child element" )
206+ entity_name = entity_el .get ("name" , "" )
207+ if not entity_name :
208+ raise ValueError ("FetchXML <entity> element must have a 'name' attribute" )
209+ return AsyncFetchXmlQuery (xml , entity_name , self ._client )
210+
151211 # --------------------------------------------------------------- sql_columns
152212
153213 async def sql_columns (
@@ -232,66 +292,6 @@ async def sql_columns(
232292 result .sort (key = lambda x : (not x ["is_pk" ], not x ["is_name" ], x ["name" ]))
233293 return result
234294
235- # --------------------------------------------------------------- fetchxml
236-
237- def fetchxml (self , xml : str ) -> AsyncFetchXmlQuery :
238- """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object.
239-
240- No HTTP request is made until
241- :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute`
242- or
243- :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages`
244- is called on the returned object.
245-
246- :param xml: Well-formed FetchXML query string. The root ``<entity name="...">``
247- element determines the entity set endpoint.
248- :type xml: :class:`str`
249- :return: Inert async query object.
250- :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery`
251- :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL
252- length limit when encoded.
253- :raises ValueError: If the FetchXML is missing a root ``<entity>`` element or name.
254-
255- Example::
256-
257- query = client.query.fetchxml(\" \" \"
258- <fetch top="50">
259- <entity name="account">
260- <attribute name="name" />
261- </entity>
262- </fetch>
263- \" \" \" )
264-
265- # Eager — all pages collected:
266- result = await query.execute()
267- df = result.to_dataframe()
268-
269- # Lazy — one page at a time:
270- async for page in query.execute_pages():
271- process(page.to_dataframe())
272- """
273- if not isinstance (xml , str ):
274- raise ValidationError ("xml must be a string" )
275- xml = xml .strip ()
276- if not xml :
277- raise ValidationError ("xml must not be empty" )
278- if len (_url_quote (xml , safe = "" )) > _MAX_URL_LENGTH :
279- raise ValidationError (
280- f"FetchXML exceeds the Dataverse URL length limit ({ _MAX_URL_LENGTH :,} characters) when encoded. "
281- "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB."
282- )
283- try :
284- root_el = _ET .fromstring (xml )
285- except _ET .ParseError as exc :
286- raise ValidationError (f"xml is not well-formed: { exc } " ) from exc
287- entity_el = root_el .find ("entity" )
288- if entity_el is None :
289- raise ValueError ("FetchXML must contain an <entity> child element" )
290- entity_name = entity_el .get ("name" , "" )
291- if not entity_name :
292- raise ValueError ("FetchXML <entity> element must have a 'name' attribute" )
293- return AsyncFetchXmlQuery (xml , entity_name , self ._client )
294-
295295 # =========================================================================
296296 # OData helpers -- discover columns, navigation properties, and bind values
297297 # =========================================================================
0 commit comments