Skip to content

Query Engine

The XrmGhost framework host answers Dataverse queries locally, against the records a scenario registers, with no organisation on the other end. A query arrives either as a QueryExpression or as FetchXML; both frontends resolve each condition to the same canonical key and consult the same condition evaluator registry, which answers — or refuses — every condition in the plan.

That is a statement about how this engine dispatches, and not about what the Dataverse platform accepts on either path — and it is a different question again from whether a FetchXML query can name an operator. The two come apart: masksselect is fully supported and has no FetchXML spelling at all, while childof has one and is refused anyway.

“Supported” is not a yes/no here. The registry declares one of three dispositions per operator, and the difference between the two negative ones is the point:

DispositionMeaningOperators
yesAn evaluator is registered and the engine answers the operator.88
no (deliberate)The engine refuses on purpose, and a measured reason says why.1
noNo evaluator is registered and nothing has been decided: a gap, not a judgement.0

Of the 89 condition operators the pinned Dataverse SDK declares, 88 are answered and none is merely uncovered. The single refusal is childof, and it rests on a fact somebody measured: the SDK reference marks ChildOf “for internal use only”, and the platform enforces that as a refusal of use rather than of the name — FetchXML parses child-of and then rejects the attribute, and a QueryExpression at the operator’s own arity answers that the operator “is not valid for standard queries”. So childof keeps its FetchXML spelling and is refused anyway: a FetchXML spelling is not support.

And the absence of a spelling is not a gap. masksselect is supported: the Dataverse server answers ConditionOperator.MasksSelect on the QueryExpression path, as the bitwise predicate (value & operand) != 0. What it has no FetchXML grammar for is the spelling — every FetchXML spelling tried is answered HTTP 400 Unknown Condition Operator — so the operator is reachable from a QueryExpression and unwritable in FetchXML. That is a fact about a frontend, and it is why masksselect counts as covered rather than refused.

Execution context: four records, defaulted and traced

Section titled “Execution context: four records, defaulted and traced”

Most operators are answerable from the record alone — equal, like, greaterthan. Some are not: “created this week” needs an instant and a first day of the week, “owned by me” needs to know who is asking. Those answers come from an execution context, which the host resolves from the same four Dataverse records a real organisation would read:

RecordWhat it answersThe documented default when it is missing
usersettingsThe caller’s time zone, business unit and language — read from uilanguageid, never from the neighbouring localeidThe query runs as UTC, with no membership and no user language
systemuserThe caller’s business unit when the execution context did not state one, and the hierarchy below them via the self-referential lookup Dataverse calls parentsystemuseridThe business unit is read from usersettings instead; with no self-referential lookup present, the hierarchy degrades to the caller alone
teammembershipThe teams of the caller and of that hierarchy, one row per (user, team) pairThe caller belongs to no team
organizationThe organisation’s first day of the week and its fiscal calendarThe week starts on Monday; the fiscal year starts 1 January and the period is monthly

Two properties of that table carry the design:

  • Nothing refuses a query for lack of context. A missing record produces a documented default, and every fallback names which record was missing — “some context was defaulted” is not a fact anybody can act on. Each line is traced through IEnhancedTracingService under TraceSource.OperationTracker, so it reaches you as a TraceEntry of the execution response. The single exception is an unanswerable user hierarchy — ambiguous, looping, or deeper than the configured cap — which refuses with MockHierarchyResolutionException before the first row of the query is read.
  • A context requirement is a resolution requirement and never an entitlement. Nothing in the requirements, the records or the resolver consults a tier, a licence flag or a plan.
PageDescription
Operator SupportAll 89 SDK condition operators row by row — canonical key, SDK member and value, FetchXML spelling where one exists, whether a FetchXML query can reach the operator, disposition, context required and the measured reason behind the single refusal — plus the three bitwise predicates and the bounds of the measurement behind them, the eight context requirements and the complete table of defaults.

This section is transcribed by hand from frozen snapshots of xrmghost-framework-host that live in this repository; there is no generator behind it.

Repoxrmghost-framework-host
Branchdev
Commitff32df7
Snapshot taken18 September 2026
SDK pinMicrosoft.CrmSdk.CoreAssemblies 9.0.2.45
AssemblyVersion of that assembly9.0.0.0 — not the pin
Inventory transcribed fromdocs/query-engine/operator-support-matrix.generated.md

AssemblyVersion 9.0.0.0 is what every 9.x package ships, so it does not identify the operator inventory this engine is written against; the package pin does. That pin is also why the count is 89: the SDK inventory grew to 90 members from 9.0.2.49 (May 2023, which added EqualRoleBusinessId), and this engine stays pinned to 9.0.2.45. The per-row detail of all 89 operators, and why two version numbers appear at all, are on Operator Support.