Repository navigation
3.0.0
XQuery upgrade facility
Breaking changes:
Correct XPath reverse axes
Problem
Previous versions have an issue with reverse axes: the ancestor,ancestor-or-self, preceding and preceding-sibling axes sometimes mistakenly returned their results in reverse document order. This has been fixed, but editors may accidentally depend on the old behavior. The error only occurred with XPath queries (or partial XPath
queries) which only contained a single step: like ancestor-or-self::* or preceding-sibling::div[@class="something"]. These XPaths used to result in a sequence containing the matching nodes, but in reverse document order. That same applies for expressions like evaluateXPathToFirstNode('ancestor-or-self::*)[1]'): this used to return the closest element ancestor of the context node. This same XPath now returns first element node in document order, which is usually the document element.
Note that XPaths with multiple steps were not affected by this bug. XPaths with the form (ancestor::div/parent::*)[1] correctly resulted in the parent of the first div, in document order: the 'topmost' one. Even XPaths as simple as ./preceding-sibling::* circumvented the bug. Also, predicates (what's between the []
brackets) are always evaluated in the order of the local step: preceding-sibling::*[1] will always return the closest sibling.
Fix
Since only XPaths with reverse axis steps are affected, a search for the reverse axis step names (ancestor, ancestor-or-self, preceding and preceding-sibling) will give you the full list of possibly affected XPaths. Secondly, since only single-step XPaths are affected, XPaths with a / on either side of the "step" can safely be
ignored. Thirdly, since only the order is changed, all XPaths which disregard order can also be ignored. This includes XPaths like count(ancestor::*).
The fix is usually as simple as adding a [last()] filter at some point of the expression:
// Expressions like:
evaluateXPathToFirstNode('ancestor-or-self::div', node, readOnlyBlueprint);
// Should be rewritten to:
evaluateXPathToFirstNode('ancestor-or-self::div[1]', node, readOnlyBlueprint);