@@ -159,10 +159,13 @@ def fetchxml(self, xml: str) -> AsyncFetchXmlQuery:
159159 :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages`
160160 is called on the returned object.
161161
162+ Use for SQL-JOIN scenarios, aggregate queries, or other operations that
163+ the OData builder endpoint cannot express.
164+
162165 :param xml: Well-formed FetchXML query string. The root ``<entity name="...">``
163166 element determines the entity set endpoint.
164167 :type xml: :class:`str`
165- :return: Inert async query object.
168+ :return: Inert async query object with ``.execute()`` and ``.execute_pages()`` methods .
166169 :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery`
167170 :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL
168171 length limit when encoded.
@@ -174,15 +177,19 @@ def fetchxml(self, xml: str) -> AsyncFetchXmlQuery:
174177 <fetch top="50">
175178 <entity name="account">
176179 <attribute name="name" />
180+ <link-entity name="contact" from="parentcustomerid"
181+ to="accountid" alias="c" link-type="inner">
182+ <attribute name="fullname" />
183+ </link-entity>
177184 </entity>
178185 </fetch>
179186 \" \" \" )
180187
181- # Eager — all pages collected :
188+ # Eager — collect all pages:
182189 result = await query.execute()
183190 df = result.to_dataframe()
184191
185- # Lazy — one page at a time:
192+ # Lazy — process one page at a time:
186193 async for page in query.execute_pages():
187194 process(page.to_dataframe())
188195 """
@@ -191,11 +198,19 @@ def fetchxml(self, xml: str) -> AsyncFetchXmlQuery:
191198 xml = xml .strip ()
192199 if not xml :
193200 raise ValidationError ("xml must not be empty" )
201+ # Fast-fail before any HTTP is attempted; execute_pages() re-checks the full URL
202+ # (base + encoded XML) on each page.
194203 if len (_url_quote (xml , safe = "" )) > _MAX_URL_LENGTH :
195204 raise ValidationError (
196205 f"FetchXML exceeds the Dataverse URL length limit ({ _MAX_URL_LENGTH :,} characters) when encoded. "
197206 "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB."
198207 )
208+ # Parse only to verify well-formedness and extract the entity name needed for the
209+ # request URL. Structural and semantic validation is intentionally left to the server
210+ # to avoid duplicating rules that may diverge from Dataverse's own enforcement.
211+ # ElementTree does not resolve external entities or expand recursive internal entity
212+ # references, so pathological inputs of that kind raise ParseError rather than
213+ # consuming resources.
199214 try :
200215 root_el = _ET .fromstring (xml )
201216 except _ET .ParseError as exc :
0 commit comments