Project

Profile

Help

Bug #4610

Bug in XPath optimizer with positional filter

Added by Clément Fournier 20 days ago. Updated 4 days ago.

Status:
Resolved
Priority:
Low
Assignee:
Category:
Internals
Sprint/Milestone:
-
Start date:
2020-06-23
Due date:
% Done:

0%

Estimated time:
Legacy ID:
Applies to branch:
10, 9.8, 9.9
Fix Committed on Branch:
10, 9.9
Fixed in Maintenance Release:

Description

Hello,

I'm using the XPath engine of Saxon HE 9.8 (JRE). The optimizer fails to optimize the following form of expression

//A[true() = false()]/*[2]

with this exception

net.sf.saxon.trans.XPathException: Axis step child::element() cannot be used here: the context item is absent
	at net.sf.saxon.expr.AxisExpression.typeCheck(AxisExpression.java:115)
	at net.sf.saxon.expr.Operand.typeCheck(Operand.java:185)
	at net.sf.saxon.expr.Expression.typeCheckChildren(Expression.java:554)
	at net.sf.saxon.expr.SubscriptExpression.typeCheck(SubscriptExpression.java:58)
	at net.sf.saxon.expr.FilterExpression.optimize(FilterExpression.java:446)
	at net.sf.saxon.expr.Operand.optimize(Operand.java:200)
	at net.sf.saxon.expr.SlashExpression.optimize(SlashExpression.java:345)
	at net.sf.saxon.expr.Operand.optimize(Operand.java:200)
	at net.sf.saxon.expr.sort.DocumentSorter.optimize(DocumentSorter.java:95)
	at net.sf.saxon.sxpath.XPathEvaluator.createExpression(XPathEvaluator.java:141)

Here is the optimizer trace:

OPT : At line 1 of 
OPT : Replaced general comparison by value comparison
OPT : Expression after rewrite: true() eq false()
OPT : At line 1 of 
OPT : Filter expression eliminated because predicate is always false
OPT : Expression after rewrite: ()
OPT : At line 1 of 
OPT : Rewriting numeric filter expression with constant subscript
OPT : Expression after rewrite: child::element()[2]
OPT : At line 1 of 
OPT : Rewrote Filter Expression as:
OPT : Expression after rewrite: child::element()[2]

The following all work and are reduced to the empty sequence:

//A[true() = false()]/*[1]


//A[false()]/*[2]
()/*[2]

The latter two being the reduction steps that the optimization of the offending query is supposed to take.

The reason is not that I'm writing literally true() = false(), but I'm inlining some statically known variables.

On Saxon HE 9, this doesn't cause an exception but the following warning:

Warning on line 1 column 5 
  Evaluation will always throw a dynamic error: Axis step child::element() cannot be used here:
  the context item is absent

and the evaluation does not throw a dynamic error.

I didn't test on previous versions of Saxon.

For reference, context around line 115 is the following:

    public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
        boolean noWarnings = ...;
        doneTypeCheck = true;
        if (contextInfo.getItemType() == ErrorType.getInstance()) {
            XPathException err = new XPathException("Axis step " + toString() +
                                                            " cannot be used here: the context item is absent");
            // ...
            throw err;
       }
    ...

This should probably have another check for whether the contextInfo is the empty sequence.

History

#1 Updated by Michael Kay 19 days ago

Reproduced on 9.8, giving

Type error on line 1 column 24 of file:/Users/mike/Desktop/temp/:
  XPDY0002: Axis step child::element() cannot be used here: the context item is absent
Static error(s) in query

On 9.9 this is reduced to a warning:

Warning on line 1 column 24 of file:/Users/mike/Desktop/temp/:
  Evaluation will always throw a dynamic error: Axis step child::element() cannot be used
  here: the context item is absent

And on 10.0 the query succeeds with no error or warning.

So it looks as if the problem has been fixed in later releases.

#2 Updated by Michael Kay 12 days ago

Rather than resting on this one, I decided to investigate why 10.0 is not reporting the warning, and it turns out that the constant ValueExpression true() eq false() is not being optimised to false(). I've fixed this, and sure enough, we now get the warning.

So now we need to fix the warning.

I think we can do this with a minor change to the sequence of operations. Currently optimization of a path expression A/B works as follows:

  1. Optimize A
  2. Determine the static type S of the post-optimization A.
  3. Optimize B, supplying S as the context item type
  4. If either A or B, post optimization, is literal (), reduce the entire expression to literal ().

The warning occurs during step 3, and can be avoided if we test whether A is literal () before doing step 3.

#3 Updated by Michael Kay 12 days ago

  • Category set to Internals
  • Status changed from New to Resolved
  • Assignee set to Michael Kay
  • Applies to branch 10 added
  • Fix Committed on Branch 10, 9.9 added

Patched SlashExpression.optimize() on both the 9.9 and 10 branches, and OptimizerEE.optimizeValueComparison() on the 10 branch only.

We're not making any further changes on the 9.8 branch unless there's something extremely severe.

#4 Updated by Michael Kay 4 days ago

Fixing the code in optimizeValueComparison has exposed another bug in code which previously wasn't being executed. The symptom is

java.lang.NullPointerException
	at net.sf.saxon.expr.CompareToIntegerConstant.effectiveBooleanValue(CompareToIntegerConstant.java:122)
	at net.sf.saxon.expr.CompareToConstant.evaluateItem(CompareToConstant.java:102)
	at net.sf.saxon.expr.CompareToConstant.evaluateItem(CompareToConstant.java:24)

in QT3 test case -s:prod-ValueComp -t:value-comp-eq-int-4

which does 40 eq xs:integer(/works/employee[830]/hours), with the path expression returning an empty sequence. We shouldn't be generating a CompareToIntegerConstant expression if the other operand is capable of returning an empty sequence, but the cardinality test is wrong.

Please register to edit this issue

Also available in: Atom PDF