Release Notes
PhoenixmlDb version history and changelog
#Unreleased: database server
#1.0.0-preview.2 (unreleased)
The database packages are not yet published on NuGet.
This version contains:
-
XQuery library modules. A query can
import module … ata file or an http(s) URL, and callfn:load-xquery-module, in the embedded engine, the gRPC server and the REST server. See XQuery Library Modules. -
One resource access allowlist,
PhoenixmlDb:ResourceAccess, for queries and stylesheets: allowed directories, allowed http(s) origins, and six size and count limits. See Resource Access. -
Stylesheets read inside the allowlist on the REST server:
xsl:import,xsl:include,doc,document, theunparsed-textfunctions,json-doc,xsl:source-documentandxsl:mergesources, from files and from http(s). -
Schema imports in queries (
import schema … at), andvalidatereturning typed nodes. -
A document name rule: a document name is a relative name inside its container, at most 507 bytes in UTF-8. See Documents & Storage: The name rule.
-
LMDB 1.0.1 storage (LightningDB 0.23.1), with detection of databases in the LMDB 0.9 format and the
PhoenixmlDb.Migratetool. See Upgrading to LMDB 1.0. -
Libraries: PhoenixmlDb.Core 2.5.0, PhoenixmlDb.XQuery 2.9.0, PhoenixmlDb.Xslt 2.9.0. See Installation.
-
Server configuration under one root,
PhoenixmlDb:*, validated at startup, with a startup summary of the effective configuration. See Server Configuration. -
REST server: error answers that lead with the XQuery/XSLT error code, schema locations for the validators, a transformation concurrency limit, and request variables for queries. See Server Mode: Requests and errors.
-
gRPC server: error text that leads with the error code.
-
UTC everywhere. A date-time without a timezone is UTC wherever the database stores, indexes or compares it; both servers and the embedded engine run queries and stylesheets in UTC. Typed dateTime metadata, dateTime index keys and API key
Expiresaccept a fixed set of forms. See Dates and Times. -
Value indexes change how fast a query answers, never the answer, and are used only by the query shapes listed in Indexing. A value index created by an earlier build is not used until it is rebuilt (warning 1025).
-
The result of a write on a cluster is one of applied, not applied, or result not known, with
Raft:ProposalTimeoutMs(default 5000). See Cluster Mode. A node stores its term and its vote in its Raft log first, and then acts on them (event 2025 when it cannot); a new leader starts its term with an empty entry (event 2024 when it cannot). The default heartbeat is 100 ms and the default election timeout is 1000–2000 ms, so a leader that fails is replaced in about 1–2 s. See Cluster Mode: Timing. -
Reads during writes. Every read of a document returns one committed version, whole. A read transaction from
BeginReadand a query each read one committed state. Read transactions, running queries and reader slots have limits (ReaderLimitException), and both servers limit the queries per API key. See Reads During Writes. -
The full-text index after a crash opens and searches without a rebuild. An index that cannot be read marks its container stale (event 1103) until it is rebuilt. See Full-Text Search: After a crash.
#Behaviour you can see
With PhoenixmlDb.XQuery 2.9.0, PhoenixmlDb.Xslt 2.9.0 and PhoenixmlDb.Core 2.5.0:
|
Area |
Behaviour |
|---|---|
|
Implicit timezone of a query or stylesheet |
UTC on every path, embedded included. See Dates and Times. |
|
|
|
|
The XSLT processor |
Schema-aware: |
|
|
The schema document, and each document it includes or imports, is read once, inside |
|
|
Not read |
|
An |
Not read, not checked, not counted against the module limits |
|
A relative |
Only the module under that base is read |
|
|
Answered like any location that is not available: HTTP |
|
|
Asks for the document |
|
|
The element is valid; its typed value is an instance of |
|
|
Returns typed nodes: atomization, comparisons, arithmetic, |
|
Atomizing a validated element with element-only content |
|
|
|
|
|
|
|
|
|
|
|
A value cast to, or atomized from, a schema-defined type |
Is an instance of that type: |
|
A date or dateTime outside the supported range |
|
|
A reserved name in a named function reference ( |
|
|
|
|
|
|
|
|
|
|
|
An import chain longer than 64 modules |
|
|
|
Work |
|
A map constructor entry whose value begins with a prefixed name ( |
Keeps its value |
|
Schema import: binding |
Refused: |
|
|
|
|
An unavailable stylesheet module |
|
For code that calls the engine's stylesheet resolver directly (the ResourceResolver of
ResourceAccessPolicy.ToStylesheetResourcePolicy): a stylesheet module that is not supplied (not
allowed, not there, not readable, past a module limit, or a relative reference with no base) is
answered with PhoenixmlDb.XQuery.Functions.XQueryException (XTSE0165), as a document
(FODC0002) or text (FOUT1170) that is not supplied is. A module past a module limit is refused
each time it is asked for.
#Known limits
-
Modules: no modules stored in the database; no compiled-module cache; a predicate inside a module function is not answered from a value index; a module loaded only at run time does not change how the query is run;
fn:unparsed-textandfn:json-docin queries read files only; XSLT cannot import XQuery modules. See XQuery Library Modules: Known limits. -
Stylesheets on the REST server: the constructs listed in Not available in stylesheets are refused, and the limits in Resource Access: Known limits apply. A
matchpattern with a syntax error inside a predicate (a[,a[1 +]) is accepted by the library. -
Validators:
sch:extends hrefand RELAX NGexternalRef/includeare not implemented. -
A cluster write answered "result not known" may still be applied. The three results of a write and what a caller does after each are in Cluster Mode: the result of a write.
-
Reads in a cluster are node-local and not linearizable.
-
The Raft log format has no version number.
-
PhoenixmlDb:Query:EnableCachingcannot be turned on. -
Migration tool: only linux-x64 is run; there is no disk-space check up front; a single value of about 2 GiB or more is untested.
#Compatibility checklist for operators
Storage and platform:
-
If your Linux host has glibc older than 2.38, the storage library does not load. Use a host with glibc 2.38 or newer, or the
aspnet:10.0image. -
If you have a database, Raft log or backup written with LMDB 0.9, opening it throws
LmdbMigrationRequiredException. RunPhoenixmlDb.Migrate; see Upgrading to LMDB 1.0. -
An over-long or otherwise refused document name throws
InvalidDocumentNameException(anArgumentException) before anything is written.
Configuration:
-
Phoenixml:DataPath,PhoenixmlDb:DataPath,Phoenixml:MaxVersionsPerDocumentandValidation:*apply for this release, with warning 3002. Rename them; see Legacy keys. -
XrxServer:*keys are ignored, with a warning. Set thePhoenixmlDb:*key the warning names. -
PhoenixmlDb:Query:EnableCaching: truestops startup. Remove it. -
PhoenixmlDb:ResourceAccess:AllowStylesheetFileAccessin the configuration stops startup. Remove it. -
Any
PhoenixmlDb:*value out of range stops startup with a message that names the key. -
Container versioning on the REST server is off by default. Set
PhoenixmlDb:Containers:Defaults:VersioningEnabled: trueor enable it per container. -
On the gRPC server,
/healthzis a deprecated alias of/healthfor one release. Use/health.
Queries:
-
fn:doc,fn:doc-available,fn:collectionandfn:uri-collectionresolve stored documents only. A file path or URL that names no stored document isFODC0002/false. -
A relative location with no base in a query or module is
XQST0059/FOQM0002/FOUT1170. Use an absolute location ordeclare base-uri. -
A query that reads files or imports modules needs the directories in
PhoenixmlDb:ResourceAccess:AllowedFileRootsand the origins inAllowedHttpOrigins. -
Every
atlocation of animport modulemust exist and hold the module. -
import schemain a library module isXQST0009. Move the import to the query. -
A missing context item for
fn:load-xquery-moduleisXPDY0002. -
A requested encoding that cannot decode a file is
FOUT1190. -
An out-of-range date is
FODT0001. -
attribute#0and similar named function references areXPST0003. -
The result of
validateis typed: comparing it as untyped (qty = '012') isXPTY0004.validate strictof an undeclared element isXQDY0084. -
A module graph that chains more than 64 modules is
XQST0059. -
A query or transformation that imports a module over http(s) reads no files. Fetch the files over http(s) too, or keep the module as a file; see Modules read over HTTP.
Stylesheets (REST server):
-
A stylesheet that reads files or URLs needs them listed in
AllowedFileRoots/AllowedHttpOrigins. -
A stylesheet that uses
fn:collection,fn:uri-collection,fn:load-xquery-module,fn:stream-available,fn:transform,fn:function-lookup,xsl:import-schemawith a location,xsl:use-package,xsl:result-document href,parameter-document,xsl:merge-source streamable, a computed shadow attribute, or the environment functions in a static expression is answered HTTP400before it runs. -
A relative
xsl:import/xsl:includein a stylesheet with noxml:baseisXTSE0165. Addxml:baseor use an absolute href. -
Inside a file module,
static-base-uri()andbase-uri()arephoenixmldb-file:///root-N/…; see How stylesheet modules read from files are named. -
An XPath syntax error in a stylesheet is HTTP
400withXPST0003(XTSE0340for a pattern). -
Transform error details begin with the error code.
-
Requests past
PhoenixmlDb:Transform:MaxConcurrentTransforms(default two per processor, 4 to 16) are answered503.
Validation (REST server):
-
A schema location (
Schemas[].Uri,xs:include,xs:import,xs:redefine,sch:include) that is not registered, not inPhoenixmlDb:Validation:BasePathand not allowed byPhoenixmlDb:ResourceAccessis HTTP400SCHEMA_LOCATION_NOT_AVAILABLE. -
Schemas kept outside the schema directory and named by absolute path need that directory in
PhoenixmlDb:ResourceAccess:AllowedFileRoots. -
PhoenixmlDb:Validation:AllowHttpResolution: truealso needs the origin inPhoenixmlDb:ResourceAccess:AllowedHttpOrigins. -
An unknown schema type is HTTP
400. -
A batch that names a schema that is not registered fails every document with
SCHEMA_NOT_FOUND. -
POST /api/v1/schemasanswers201. -
Updating, deleting or activating schemas on
/api/schemasneeds an admin key; a write key gets403. -
A document nested more than 1024 elements deep is a validation error; a schema nested more than 512 deep is refused.
Documents:
-
Document names with a leading
/, a drive, a URI scheme followed by/,file:,phoenixml:,phx-schema:, a.or..segment, a control character, or more than 507 bytes are refused on write. One-letter prefixes such asa:bare the drive form and are refused. -
Documents already stored under such names stay readable and deletable; put them under an accepted name to update them.
-
Tools that derive names from file paths must pass relative names without
.or...
gRPC clients:
-
The
errortext of a failed query with an error code begins with the code. -
After "result not known" (
WRITE_OUTCOME_UNKNOWN:), read first; send a write again only after "not applied".
Cluster:
-
Migrate each node's database directory and Raft log directory. A node on the new build refuses a Raft snapshot sent by a node still on LMDB 0.9.
-
A Raft-enabled server needs API keys and a Raft port different from both client ports.
#Engines: PhoenixmlDb.Xslt and PhoenixmlDb.XQuery
Since 2.0.0 the two engines release together as one train: the Xslt and XQuery versions match, and each release pins the other at the same version. The full per-version notes live in each repo: Xslt · XQuery. The summaries below lead with each release's measured W3C figures, as recorded when it shipped.
On comparing figures across releases. The test harness was corrected more than once. On 2026-09-04, expected-error tests had been scored as passes on any error, so earlier XSLT figures are overstated. Later corrections changed the case count (denominator). A figure is exact for its own release; subtracting across a harness change compares different measurements.
#3.1.0 (2026-11-12): XQuery and Xslt 3.1.0, Core 2.6.0
W3C QT3: 31 failing of 31,325 · W3C XSLT 3.0: 131 failing of 11,539 (132 at 3.0.0, measured on a host in UTC).
XQuery 3.1.0 requires Core 2.6.0; Xslt 3.1.0 requires XQuery 3.1.0 and Core 2.6.0. No breaking changes.
Security. Hosts that compile or run untrusted queries or stylesheets must upgrade. See GHSA-52r3-6pq9-42m5 (XQuery) and GHSA-55vh-9gg8-hqc4 (Xslt) for the details and workarounds.
-
A limit on the length of a built string:
QueryExecutionLimits.MaxStringLength(XQuery),XsltTransformOptions.MaxStringLengthandXsltTransformer.MaxStringLength(Xslt). Over the limit isFOER0000. See Resource Policy: String Length. -
XsltTransformer.LastTransformStreamedtells whether the last transformation streamed. The--timinglabel of the CLI comes from it. -
Streaming holds memory flat in more stylesheets: the
TextReaderoverloads and the CLI's standard output stream;accumulator-after()of the document node, an absolute path in amatch="/"rule, and an aggregate over a value computed from each element stream. A streamedxsl:source-documentwithuse-accumulators="#all"runs the accumulators. -
A prefixed name parses in linear time: 10,000 calls such as
fn:abs(1)compile in 0.19 s. -
fn:function-lookupinsidexsl:evaluateraises XTDE3160 for the XSLT-defined functions, as a direct call does.
The complete lists are in the release notes of PhoenixmlDb.XQuery 3.1.0 and PhoenixmlDb.Xslt 3.1.0.
#3.0.0 (2026-11-04): XQuery and Xslt 3.0.0, Core 2.6.0
W3C QT3: 31 failing of 31,325 (207 at 2.9.0) · W3C XSLT 3.0: 132 failing of 11,539 (216 at 2.9.0, measured on a host in UTC). Both figures include corrections to the test harness as well as engine changes.
XQuery 3.0.0 requires Core 2.6.0; Xslt 3.0.0 requires XQuery 3.0.0 and Core 2.6.0. The packages share one version: the release train moves to 3.0.0 because Xslt 3.0.0 has a breaking API change.
Security. Hosts that compile queries, stylesheets or xsl:evaluate expressions from untrusted
parties must upgrade. See
GHSA-9qvr-vjmg-m3pf
(XQuery) and
GHSA-6342-6rv2-xj9j
(Xslt) for the details and workarounds.
Breaking changes, check them when you upgrade:
-
Xslt: the namespace sets of three AST types are read-only.
XsltElement.InScopeNamespaces,XsltAttribute.InScopeNamespacesandXsltLiteralResultElement.NamespaceDeclarationsareIReadOnlyDictionary<string, string>. Code that changes them, or takes them as aDictionary, does not compile, and the change is binary-breaking. -
XPDY0130 for an expression nested too deeply for the thread's stack. A caller can catch it. A long chain of arithmetic, comparison,
and,oror||operators evaluates on a new stack and does not raise it. In browser WebAssembly and WASI, which cannot start a thread, evaluation stays on the current stack. -
E => f(A)parses to the function callf(E, A). Code that walks the AST and looks forArrowExpressionto find=>must look for the call;ArrowExpressionis the thin arrow->. -
FODC0005 for a colon in the first segment of a relative
fn:docreference, for exampledoc('2026-01-02T10:30.xml'). Start the reference with./to read such a name:doc('./2026-01-02T10:30.xml'). -
as="element(N, T)"checks the type annotation in XSLT: a stylesheet that declares a wrong type in this form can fail. -
A
document-node(element(N, T))pattern tests the type of the document element: a rule for a schema type does not match a source that is not validated. Validate the source (XsltTransformer.SourceValidation, or--validation strict) to get the typed match. -
CostModel.EstimateCardinalitykeeps its results perCostModelinstance; do not share one instance between threads.XQueryExpressionRewriter.RewriteandXQueryExpressionWalker.Walkcan continue on another thread when the stack is low, which matters to a subclass with[ThreadStatic]state.
The complete lists are in the release notes of
PhoenixmlDb.XQuery 3.0.0
and PhoenixmlDb.Xslt 3.0.0.
The xquery4 tool 3.0.0 runs on Xslt 3.0.0, and the xquery-mcp and xslt-mcp servers follow the
3.0.0 engines.
#2.9.0 (2026-10-15): XQuery and Xslt 2.9.0, Core 2.5.0
W3C QT3: 207 failing of 31,331 (251 at 2.8.0) · W3C XSLT 3.0: 216 failing of 11,539.
The XSLT figure is not comparable with 2.8.0's. From this release the conformance runner measures a schema-aware processor, so the suite includes about 700 schema-aware cases that the run for 2.8.0 (131 failing of 10,839, as a basic processor) skipped. When the runner first measured the schema-aware suite it was 437 failing; the schema work in this release brought it to 216.
XQuery 2.9.0 requires Core 2.5.0; Xslt 2.9.0 requires XQuery 2.9.0 and Core 2.5.0.
Behaviour changes, check before upgrading:
-
The XSLT processor is schema-aware by default. A
typeattribute andvalidation="strict"are accepted in a stylesheet with noxsl:import-schema, andsystem-property('xsl:is-schema-aware')isyes. A stylesheet that relied onXTSE1660now compiles.XsltTransformer.SchemaAware = falsegives the basic processor again. -
xsl:value-ofof a validated node gives its typed value: an element of typexs:decimalwith the text1.10gives1.1. -
order bycompares dates, times and durations by value. They were ordered as text:P10DbeforeP9D.eqonxs:gYear,xs:gYearMonth,xs:gMonth,xs:gMonthDayandxs:gDayuses the implicit timezone for a value with none (it took UTC), and XSLTkey()finds dates and times aseqcompares them. -
XSLT validates where the node is built, with XSLT error codes (#353, #354). An element with
[xsl:]validationin a variable, a parameter or a function is validated there and keeps its type annotations. A failure isXTTE1510,XTTE1512orXTTE1515, notXQDY0027: a stylesheet or host that tests forXQDY0027must test for the new codes.default-validation="strict"isXTSE0020(wasXTSE1660), a malformedtypeisXTSE1520(wasXTSE0280), and an invalidxsi:typeunder lax validation isXTTE1510(wasXTTE1515). See Validation error codes in XSLT. -
validate type T { … }and the XSLTtypeattribute validate against the type, whatever is declared for the element's name. The type was not used, and an element with no declaration passed with any content. In XSLT a node that is not valid isXTTE1540, and a type no imported schema declares isXTSE1520. -
A match pattern that names a type matches by the type (
element(*, T),schema-element(N)and the like). The rule matched every node of the name. -
A validated element is not held to the ID constraints of a document (XQuery
validate, XSLT[xsl:]validation), and a validated element of a type with element-only content has no whitespace text children. -
attribute(name)with an unprefixed name matches only an attribute in no namespace. -
StrictStreamability/--strict-streamingis now the complete analysis of XSLT 3.0 §19.8. A stylesheet that passed the 2.8.0 check can fail it. Off by default as before. See XSLT API for what it accepts beyond the specification and what it does not check. -
Streamed bodies of a shape not known to stream correctly are buffered. Of about 600 distinct streamed bodies in the W3C streaming sets, 28 now buffer the whole input. A source validated with
SourceValidationor byxsl:source-document validation=is not streamed. -
The timezone settings default to the machine.
QueryEngine.ImplicitTimezoneand the XSLT settings arenullunless a host sets them, which is the machine's timezone as before. -
Imports and module base URIs.
xsl:importandxsl:includeare resolved against the base URI in effect at the element (xml:base), and the resolver is asked once (2.8.0 asked twice, the first time for a location the stylesheet does not name). A module thatuse-whenexcludes is not read. A relative schema location in a stylesheet with no base URI is an error. An XQuery library module has its own static base URI (#223); it used the main module's. -
Error codes where a .NET exception came out (#241): a function of the wrong arity given to
for-each,filter,array:for-each,array:filter,fold-leftorfold-rightisXPTY0004;array:headorarray:tailof an empty array, and an array index too large for an integer, areFOAY0001; too large an arity in a named function reference isFOAR0002; a function item given toconcatisFOTY0013. -
Core 2.5.0: pattern and length facets count characters by code point, so a validation result can change for a value with a character outside the Basic Multilingual Plane.
Changes to serialized output:
-
xsl:value-ofof a validated node writes its typed value (1.10asxs:decimalwrites1.1). -
order byon dates, times and durations can put results in a different order. -
A validated element whose type has element-only content has no whitespace text children, so the whitespace between its children is not in the output.
-
XSLT streamed output that was wrong with no error is right: template rules of a streamable mode that lost end tags or values (#340), and three more shapes (#343), among them a streamed
xsl:for-each select="foo"that ran for every child element. -
TransformAsync(Stream, TextWriter)holds the output of the rule for the document node until the rule ends:<out>1;2;</out>came out as1;2;<out/>. -
What a failing global variable that nothing used wrote is no longer ahead of the result.
-
Core 2.5.0 does not change serialized output.
Fixed (selected; the Xslt and XQuery notes list all):
-
XSLT schema-aware processing.
as=checks derived built-in types (xs:int) and schema-defined atomic types;fn:copy-of,fn:snapshotandxsl:copy-of validation="preserve"keep annotations;xsl:source-document validation=,input-type-annotations="strip", typed modes andtypeonxsl:attribute,xsl:copyandxsl:copy-of(#364) are applied;json-to-xmlwithvalidate: true()gives a typed tree; an inline schema with no target namespace is loaded; a type of a schema with no target namespace can be named (#359). -
An imported schema is compiled once for all transformers (#355); each
XsltTransformercompiled everyxsl:import-schemaagain. -
XQuery schema-aware typing:
fn:distinct-values,fn:data,sum,avg,minandmaxof validated and list-typed nodes;xs:unsignedLongabovelong;xs:NOTATIONtyped values; union types inelement(*, U); negative and five-digit years in dates; a nilled element atomizes to the empty sequence (#236); kind tests in host-language expressions take the host's prefixes and default namespace. -
XSLT streaming:
xsl:iterateandxsl:for-each-groupunder the document node read the input record by record, andxsl:breakstops the reading;xsl:on-completionis not run afterxsl:break;count(//item)in a variable is no longerXTSE3430;lang()andxsl:for-each select="head(//x)"are streamable. -
xsl:with-paramwith a body and a one-nodeastype passes that node (#358). -
file:/abs/pathworks indoc,doc-availableandjson-doc(#229). -
group byfinds a tuple's group by hash, not by comparing with every group (#232).
New API (see Schemas and Validation, XSLT API, Hosting the Engines and Resource Policy):
-
Implicit timezone:
QueryEngine.ImplicitTimezone,QueryExecutionContext.ImplicitTimezone,XsltTransformer.ImplicitTimezone,XsltTransformOptions.ImplicitTimezone. -
XSLT:
XsltTransformer.SchemaAware,SourceValidation(also onXsltTransformOptions),PrincipalResultTree(XsltResultTree), andXsltStylesheetSchema, an XSD 1.0 schema for XSLT 3.0 stylesheets. -
ResourcePolicyBuilder.AllowTransformFunction(false)/ResourcePolicy.AllowTransformFunctionturnfn:transformoff (FOXT0001); on by default.IHostGatedFunction. -
Schemas:
XsdSchemaProvider.KnowsXmlNamespace,XsdSchemaProvider.DeclaringEntities,ISchemaProvider.ValidateElementAndAnnotate, andImportSchema/AddSchemaTextoverloads that take the importing module's URI. -
ResourceRequest.ModuleUriis set for the requests a query or the XSLT compiler makes (#227, #335), andXQueryExpression.EmbeddedInModule(#230).
Core 2.5.0 adds PhoenixmlDb.Xdm.ImplicitTimezone (Current, Use(offset)), which the date
and time types read where they read the machine's offset; nothing changes for a host that does not
set it. Pattern facets and the length, minLength and maxLength facets of string types count
characters by code point (see
Schemas and Validation).
#2.8.0 (2026-10-09): XQuery and Xslt 2.8.0, Core 2.4.0
W3C QT3: 251 failing of 31,331 (257 at 2.7.0) · W3C XSLT 3.0: 131 failing.
Security fixes in 2.8.0. Only hosts that run queries or stylesheets from untrusted parties are affected. XQuery: GHSA-g2xr-r7jg-2v33: every HTTP redirect is authorized against the resource policy, XInclude reads under the policy, and refusals no longer disclose host paths. The XSLT advisory will be linked here when it is published. Upgrade to 2.8.0.
Core 2.4.0 is a breaking release for code that builds NodeReader or reads stored nodes
directly: new NodeReader(buffer) is removed, and reading the string value of a node with
children but no resolver now throws instead of returning "" (switch
PhoenixmlDb.Xdm.StrictStringValue to false for the old behaviour). The XQuery and XSLT engines
are unaffected.
The shared schema layer. Core 2.3.0 added PhoenixmlDb.Core.Schema (sources, an access gate,
a compiler, a cache, a validator, XML Catalogs), and in 2.8.0 the engines read every schema
through it (see Schemas and Validation):
-
Schemas imported with
import schemaare compiled once and shared across queries; a changed schema file or included file is picked up on the next use. Importing a 705 KB schema: 39.7 ms per query before, 1.6 ms now. In 2.8.0xsl:import-schemawithXsltTransformer's default schema provider compiles its schemas per transformer; to share one compiled schema, give the transformer anXsdSchemaProvidermade from aCompiledSchema(see Schemas and Validation). -
Stricter loading, check on upgrade: a schema include or import that cannot be read fails the load (
XQST0059) instead of being skipped; a schema document with a DOCTYPE is refused; schema text without a base URI no longer resolves relative locations against the current directory. -
A schema that requires XSD 1.1 now says so, naming the document and the constructs. The processor implements XSD 1.0.
-
An atomic value keeps its schema type (
t:size('8') instance of t:sizeis true), and in XSLT the constructor functions of imported schema types can be called.
Other changes:
-
XSLT: an inline
xs:schemainxsl:import-schemaandxsl:validationon literal result elements are applied;StrictStreamability(opt-in,--strict-streaming) reportsXTSE3430, and two wrong results in streamed rules are fixed. -
A cancelled query no longer starts a regex or pattern match.
-
The
xquery4CLI writesxmlns=""correctly (#105). -
New API:
JsonXmlConverter,XsdSchemaProvider(CompiledSchema),XsdSchemaProvider.Catalog,ISchemaProvider.AddSchemaText.
#2.7.0 (2026-10-07): XQuery and Xslt 2.7.0, Core 2.2.0
W3C QT3: 257 failing of 31,331 (427 at 2.6.0) · W3C XSLT 3.0: 131 failing (unchanged).
Security fixes in 2.7.0 (GHSA-6whf-hvfp-757r, GHSA-xxjq-rwpx-m5ww). Only hosts that run queries or stylesheets from untrusted parties are affected.
A module loaded with
fn:load-xquery-modulenow runs inside the caller's limits, cancellation token and host function replacements. Module import chains and nestedload-xquery-modulecalls are capped at 64 (XQST0059,FOQM0003); schema nesting at 512.
RegexMatchTimeoutand cancellation now reach XSLT static expressions, nestedfn:transformandfn:transformfrom a query, and bound XSDpatternfacets in casts, validation and schema loading. NewCompilationOptions.RegexMatchTimeoutandXsdSchemaProvider.PatternMatchTimeoutfor schemas imported at compile time.
fn:parse-xmlis linear in nesting depth, can be cancelled, and no longer overflows the stack on deeply nested input, including on Windows.A schema's own includes and imports can be checked against a resource policy (
XsdSchemaProvider.Add/AddFromStringoverloads), and a text load the policy refuses ends withFOUT1170instead of being read anyway.New
IResourceResolver.ResolveContentandSuppliesAllContent: a host can supply the content of everything the engines load, which removes the gap between checking a location and opening it. See Resource Policy.Not fixed: when the host neither refuses a location nor supplies its content, a file can still be replaced between the check and the open.
Schema-aware typing. A validated node now carries its schema type through atomization, type
tests, casts and fn:idref:
-
data()of a validated node gives values of its schema type (including restrictions, lists and unions); a node with element-only content has no typed value (FOTY0012). -
Validated nodes match
element(*, p:T),schema-element(p:e)andschema-attribute(p:a), substitution groups included. -
Casts to schema-defined list and union types work;
fn:idreffinds schema-typedIDREFnodes. -
Nodes that were not validated are unaffected.
Behaviour changes, read before upgrading:
-
validate strictof an element with no declaration isXQDY0084. -
A date or dateTime out of range is
FODT0001(wasFORG0001); a reserved name in a named function reference such asattribute#0isXPST0003(wasXPST0017). -
fn:unparsed-textwith a named encoding reportsFOUT1190for content that is not valid in it, andfn:unparsed-text-availableanswers false for an encoding the runtime refuses. -
A schema import's static rules are checked first:
XQST0070,XQST0057andXQST0058where each used to beXQST0059. -
An imported module file must declare the namespace it was imported for (
XQST0059), and an external variable with no value isXPDY0002.
Fixed:
-
An element parsed with
xmlns=""keeps its undeclared default namespace infn:serializeoutput and inin-scope-prefixes()(#105). Known issue: thexquery4CLI's own output still dropsxmlns=""in that case; usefn:serializeuntil it is fixed. -
A string returned through
fn:transformis no longer parsed as XML with raw delivery, a regression since 2.4.0 that XSpec suites run withrun-as="external"hit (#314). -
$err:codeand other qualified local variables work in library modules; a map constructor entry whose key begins with a prefixed name keeps its whole value.
New API: synchronous XsltTransformer.LoadStylesheet / Transform for desktop and plugin hosts
(see Hosting the Engines), IResourceResolver.ResolveContent
/ SuppliesAllContent, and ISchemaProvider.HasIdrefTypedValue (default false; custom providers
compile unchanged).
#2.6.0 (2026-10-05): XQuery and Xslt 2.6.0, Core 2.1.0
W3C XSLT 3.0: 131 failing (216 at 2.5.1) · W3C QT3: 427 failing (708 at 2.5.1).
Security fixes in 2.6.0. A cancelled query or transformation now stops inside regex matching, sorting and XPath evaluation (GHSA-2wrh-863w-x2m9, GHSA-h2xc-4m53-6j8r). A .NET regex match can't observe a
CancellationToken, and the engines built every regex with no match timeout, so a pattern with catastrophic backtracking kept running after cancellation, for a time exponential in the input; sorts ran to completion first; andXsltTransformerdidn't pass its token into XPath evaluation at all. A host enforcing a time limit by cancelling was not protected.
New:
QueryExecutionLimits.RegexMatchTimeout(XQuery), andXsltTransformer.RegexMatchTimeout/XsltTransformOptions.RegexMatchTimeout(XSLT). They are opt-in: the default isnull, so behaviour is unchanged until a host sets one. A match that runs past the timeout fails withFOER0000, or withOperationCanceledExceptionwhen the query was cancelled meanwhile.Sorts (
fn:sort,order by,array:sort,xsl:sort) check the token as they run, and the token and query limits reach every XPath evaluation in a transformation.A match already running still can't be interrupted before its timeout; choose a timeout no longer than the latency you can accept after a cancel.
Hosts that run untrusted queries or stylesheets under a time limit should set
RegexMatchTimeout.
Behaviour changes, read before upgrading:
-
fn:format-numberrounds half to even, as the specification requires:format-number(2.5, '0')is now2. -
Errors that previously surfaced with the wrong code now use the specified ones, for example
XPTY0019for a path step whose left operand isn't a node, and the specified cast error codes. -
Indented output is LF on every OS (engines and Core); it was CRLF on Windows.
-
Opt-in static typing (
StrictTypeChecking) is now implemented.
Fixed:
-
Streaming: a streamed template that read its children as a value (
{foo}) lost them, andxsl:copywith such content produced malformed output; a buffered streamed subtree dropped whitespace-only text; a template that reads outside the matched element was streamed with wrong results instead of being evaluated on a tree (Martin Honnen's #295 and #301; #298). -
--no-streamreally evaluates against a tree, via the newXsltTransformer.DisableStreaming.
Also changed: LoadStylesheetAsync now completes on a thread-pool thread, as TransformAsync
already did, because compilation moved onto the engine's large-stack thread. Hosts with a UI
context are unaffected. A host with no SynchronizationContext, such as an editor plugin, now
continues off its UI thread after that await. See
Hosting the Engines.
-
Browser WebAssembly: XSLT runs again in Blazor WebAssembly (#237, a 2.5.1 regression), and deep recursion there no longer exhausts the stack.
-
Schematron performance: applying compiled ISO Schematron validators allocates about 8× less and runs 2–3× faster than 2.5.1, measured on the published 2.6.0 packages against real EMS validators and payloads, with output identical to Saxon's.
-
DocBook xslTNG: cross-references, annotations and tables of contents that 2.5.1 rendered incompletely.
New API: XsltTransformer.DisableStreaming, AllowedOutputMethods, OutputDeclarations and
XQueryModules; Core XdmValue.From(DateTime). See the
XSLT API.
#2.5.1 (2026-10-01): XQuery 2.5.0 and 2.5.1, Xslt 2.5.1
W3C XSLT 3.0: 216 failing (442 at 2.4.0) · W3C QT3: 708 failing (1484 at 2.4.0). Much of the QT3 drop comes from correcting the test harness, which had scored some correct results as failures, so the 2.4.0 figures were measured before those corrections (see the note on comparing figures above).
Security fixes in 2.5. PhoenixmlDb.XQuery 2.5.0 and PhoenixmlDb.Xslt 2.5.1 fix advisories GHSA-wjxc-7p24-xf7w (XQuery) and GHSA-86rg-wxgp-9p5j (XSLT). In earlier versions, a configured
ResourcePolicy,ServerDefaultincluded, was enforced only when loading documents, so queries and stylesheets could read files or fetch URLs the policy forbade, through functions likeunparsed-text,json-doc, module and schema imports,xsl:source-document,fn:transformand HTTP redirects. In 2.5.x every read, fetch and dynamic evaluation is checked against the policy, at load time as well as at run time. With no policy configured, nothing changes. Hosts that run untrusted queries or stylesheets should upgrade XQuery and Xslt together to 2.5.1.
There is no Xslt 2.5.0. Xslt's version follows the XQuery it's built on, and XQuery needed a 2.5.1
patch, found by this release's own testing, before Xslt could ship. The xquery CLI ships as
cli-v2.5.1.
Behaviour changes, read before upgrading:
-
Resource policy rules are stricter when a policy is set: read access no longer implies import access, an empty rule list denies, a host rule admits only the default port unless one is given, and path prefixes match whole segments. See Resource Policy.
-
An accumulator that isn't applicable to the principal source document raises
XTDE3362, as in Saxon (#213). Add it to the initial mode'suse-accumulators. -
The
unparsed-textfamily resolves relative URIs against the calling module and raisesFOUT1170(#195).
Fixed and added:
-
Streaming: grouping over attributes and text, streamable functions, attribute-only
current-group(), and accumulator rules. -
Grouping focus inside
apply-templatesfollows XSLT 3.0 §14.2: kept, except within a declared-streamable construct. -
system-property()andelement-available()resolve prefixes where the function item was created (+22 W3C cases). -
Schemas: schema-defined simple types as item types and constructors,
json-to-xmlvalidation, XSD 1.1 conditional inclusion, and reliablexml:idon every runtime. -
Deep documents no longer crash the process.
-
Many errors that surfaced as raw .NET exceptions now raise the codes the specifications assign.
#2.4.1 (2026-09-28): patch
-
Fixed a 2.4.0 regression that broke XSpec compilation. A string subtype such as
xs:NCNamehad no effective boolean value, and 2.4.0 had madeprefix-from-QNamereturn one. Fixed in both engines, along with string-subtype map keys,fn:translateandfn:collation-key. -
New release gate: every release is now checked against real-world stylesheets (the XSpec compiler, DocBook xslTNG, reported repros) on the previous version and the candidate before it ships.
#2.4.0 (2026-09-28)
W3C XSLT 3.0: 10,397 / 10,839 (95.92%) · W3C QT3: 29,895 / 31,379 (95.27%)
Behaviour changes, read before upgrading:
-
fn:namespace-urireturnsxs:anyURI,fn:prefix-from-QNamereturnsxs:NCName, andfn:default-languagereturnsxs:language, as the spec requires. They previously returned plain strings. -
fn:distinct-valuestreats anxs:anyURIand an equalxs:stringas one value. -
The
xqueryCLI rejects an unknown output method (exit code 1, orSEPM0016in the prolog) instead of silently producing adaptive output. It gainshtmlandxhtml.
Fixed:
-
Streaming:
xsl:messagecontent, templates below unmatched elements, subtree ancestors,has-children(),xsl:strip-space/xml:space, early-closing parent tags, and unrequested child processing. Closes the three streaming sets that had fallen below their 1.6.10 scores. -
fn:transformhonourssource-location, and raw delivery from XQuery returns the nodes a template constructs instead of nothing. -
CLI output is UTF-8 on every platform. On Windows it followed the console code page, which corrupted non-ASCII characters in redirected output.
There is no Xslt 2.3.0. XQuery 2.3.0 (below) was published and then superseded, and both engines moved to 2.4.0 together.
#2.3.0 (2026-09-28): XQuery only, superseded
A lockstep version bump; the library is byte-identical to 2.2.0. Moving from 2.2.0 straight to 2.4.0 is the intended upgrade path.
#2.2.0 (2026-09-24/25)
W3C XSLT 3.0: 10,384 / 10,839 (95.80%) · W3C QT3: 29,803 / 31,379 (94.98%)
-
xs:IDREFS,xs:NMTOKENSandxs:ENTITIESare valid cast and castable targets (+63 QT3). -
xsl:exposeselects components correctly. -
A match pattern with any predicate was O(n²) in sibling count: 12.6× faster at 4,000 siblings.
-
Streamed
group-adjacentkey checks,xsl:sort/xsl:keytemporary output state,regex-group()in patterns, text nodes returned byxsl:function, and static declarations coming into scope in declaration order.
#2.1.0 (2026-09-17)
W3C XSLT 3.0: 10,347 / 10,839 (95.46%) · W3C QT3: 29,822 / 31,379 (95.0%), as recorded in the XQuery 2.1.0 notes.
Breaking: a typed variable whose body produces the wrong number of items raises XTTE0570.
xsl:strip-space / xsl:preserve-space in imported modules are applied, with conflicts resolved
by import precedence. xsl:map-entry keys are atomized.
#2.0.0 (2026-09-15)
W3C QT3: 29,813
Major because the train is: PhoenixmlDb.Core, PhoenixmlDb.XQuery and PhoenixmlDb.Xslt
moved to 2.0.0 together.
-
Breaking: built-in functions apply argument cardinality, and their signatures match F&O 3.1, so some existing queries stop compiling. The extension functions moved.
-
In-scope namespaces survive copies and serialization. Serialization parameters are read properly. The XSLT-side
fn:serializeoverride is retired.
#1.8.0 (2026-09-13)
W3C XSLT 3.0: 10,292 / 10,630 (96.82%) · W3C QT3: 29,534 / 31,414 (94.02%). This is a different denominator from 2.x.
Silent wrong answers fixed: xsl:function cache="yes" returning other calls' results;
accumulators reading a later-declared accumulator one node late; as="item()*" functions losing
element nodes; xsl:merge merging only one of two sources; and xsl:try running its body in the
caller's scope.
#1.7.0 (2026-09-10)
W3C XSLT 3.0: 10,082 / 10,630 (94.8%)
-
fn:sumreturned 0 forxs:integervalues cast from text. -
Aggregates over storage-backed elements atomized to
"". -
map:put/map:remove/map:replacecopied the whole map on every update (O(n²)). -
A caller's timeout could not stop recursion, callbacks or a slow
every. -
Several
contains textdefects.
#1.2 – 1.6 (April – September 2026)
A long run of frequent, often daily, releases: over a hundred versions across both engines. That
cadence has since been replaced by releases that each carry a complete batch. Highlights:
serialization conformance (character maps, HTML/XHTML output methods), streaming, QT3 production
sweeps, and input hardening (parser recursion bounds, XQST0090 on invalid character references,
concurrent namespace interning).
Figures in this period predate the 2026-09-04 harness correction. Each version's notes are in
the Xslt and XQuery repos.
#Contributing
Contributions to the open-source XQuery and XSLT engines are welcome. See the contributing guide.
#License
PhoenixmlDb.Core, PhoenixmlDb.XQuery and PhoenixmlDb.Xslt, with their command-line tools and MCP servers, are licensed under the Apache License 2.0. The PhoenixmlDb database engine is licensed under the PhoenixmlDb Developer License Agreement from Endpoint Systems.
#Acknowledgments
PhoenixmlDb builds on these excellent projects:
-
LMDB - Lightning Memory-Mapped Database
-
LightningDB - .NET bindings for LMDB
-
ANTLR4 - Parser generator
Special thanks to the W3C for the XQuery, XPath, XSLT, and XDM specifications.