Query Engine
Query Engine
Section titled “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.
Coverage, in three dispositions
Section titled “Coverage, in three dispositions”“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:
| Disposition | Meaning | Operators |
|---|---|---|
yes | An evaluator is registered and the engine answers the operator. | 88 |
no (deliberate) | The engine refuses on purpose, and a measured reason says why. | 1 |
no | No 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:
| Record | What it answers | The documented default when it is missing |
|---|---|---|
usersettings | The caller’s time zone, business unit and language — read from uilanguageid, never from the neighbouring localeid | The query runs as UTC, with no membership and no user language |
systemuser | The caller’s business unit when the execution context did not state one, and the hierarchy below them via the self-referential lookup Dataverse calls parentsystemuserid | The business unit is read from usersettings instead; with no self-referential lookup present, the hierarchy degrades to the caller alone |
teammembership | The teams of the caller and of that hierarchy, one row per (user, team) pair | The caller belongs to no team |
organization | The organisation’s first day of the week and its fiscal calendar | The 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
IEnhancedTracingServiceunderTraceSource.OperationTracker, so it reaches you as aTraceEntryof the execution response. The single exception is an unanswerable user hierarchy — ambiguous, looping, or deeper than the configured cap — which refuses withMockHierarchyResolutionExceptionbefore 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.
Pages in this section
Section titled “Pages in this section”| Page | Description |
|---|---|
| Operator Support | All 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. |
Provenance
Section titled “Provenance”This section is transcribed by hand from frozen snapshots of xrmghost-framework-host that live
in this repository; there is no generator behind it.
| Repo | xrmghost-framework-host |
| Branch | dev |
| Commit | ff32df7 |
| Snapshot taken | 18 September 2026 |
| SDK pin | Microsoft.CrmSdk.CoreAssemblies 9.0.2.45 |
AssemblyVersion of that assembly | 9.0.0.0 — not the pin |
| Inventory transcribed from | docs/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.