Project

Profile

Help

Bug #2225

closed

Optimization of filter expressions in 1.0 mode

Added by Michael Kay about 10 years ago. Updated almost 9 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
XSLT conformance
Sprint/Milestone:
Start date:
2014-11-19
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
9.6
Fix Committed on Branch:
9.6
Fixed in Maintenance Release:
Platforms:

Description

Saxon-EE attempts optimization of filter expressions having the form $xxx[@id = $yyyy]. The optimization builds an index and uses this index to speed up evaluation of the expression. This optimization should not be attempted if the stylesheet is running in XSLT 1.0 backwards compatibility mode (i.e. if the stylesheet specifies version="1.0", because the semantics of general comparisons are then different (for example, if $yyyy is a boolean value true/false, then the test selects $xxx values for which @id is present/absent). However, the optimization is attempted, and may cause incorrect behaviour: either incorrect results, or a subsequent compile-time failure such as:

java.lang.ClassCastException: net.sf.saxon.expr.GeneralComparison10 cannot be cast to net.sf.saxon.expr.ComparisonExpression

   at com.saxonica.ee.optim.IndexedFilterExpression.replaceOperand(IndexedFilterExpression.java:252)

   at net.sf.saxon.expr.Expression.simplify(Expression.java:139)
Actions #1

Updated by Michael Kay about 10 years ago

I have committed a patch on the 9.6 and 9.7 branches. The effect of the patch is to suppress optimization of filter expressions in stylesheets that specify version="1.0".

Actions #2

Updated by Michael Kay about 10 years ago

  • Status changed from In Progress to Resolved
Actions #3

Updated by O'Neil Delpratt almost 10 years ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in version set to 9.6.0.3

Bug fix patch applied to the Saxon 9.6.0.3 maintenance release

Actions #4

Updated by Radu Pisoi over 9 years ago

Unfortunately, this issue can be reproduced again by using Saxon 9.6.0.5.

To reproduce it, run from command line:

D:\workspace\eXml>java -Xmx60m -cp lib/saxon9ee.jar net.sf.saxon.Transform -xsl:samples/test.xsl -s:samples/test.xml
java.lang.ClassCastException: net.sf.saxon.expr.GeneralComparison10 cannot be cast to net.sf.saxon.expr.ComparisonExpression
        at com.saxonica.ee.optim.IndexedFilterExpression.replaceOperand(IndexedFilterExpression.java:252)
        at net.sf.saxon.expr.Expression.simplify(Expression.java:139)
        at net.sf.saxon.expr.Expression.simplify(Expression.java:137)
        at net.sf.saxon.expr.Expression.simplify(Expression.java:137)
        at net.sf.saxon.expr.parser.ExpressionVisitor.simplify(ExpressionVisitor.java:158)
        at net.sf.saxon.expr.GeneralComparison.typeCheck(GeneralComparison.java:314)
        at net.sf.saxon.expr.parser.ExpressionVisitor.typeCheck(ExpressionVisitor.java:188)
        at net.sf.saxon.expr.GeneralComparison10.optimize(GeneralComparison10.java:168)
        at net.sf.saxon.expr.parser.ExpressionVisitor.optimize(ExpressionVisitor.java:244)
        at net.sf.saxon.expr.FunctionCall.optimize(FunctionCall.java:168)
        at net.sf.saxon.functions.SystemFunctionCall.optimize(SystemFunctionCall.java:238)
        at net.sf.saxon.functions.NotFn.optimize(NotFn.java:64)
        at net.sf.saxon.expr.parser.ExpressionVisitor.optimize(ExpressionVisitor.java:244)
        at net.sf.saxon.expr.LetExpression.optimize(LetExpression.java:250)
        at net.sf.saxon.expr.parser.ExpressionVisitor.optimize(ExpressionVisitor.java:244)
        at net.sf.saxon.expr.instruct.Choose.optimize(Choose.java:393)
        at net.sf.saxon.expr.parser.ExpressionVisitor.optimize(ExpressionVisitor.java:244)
        at net.sf.saxon.style.XSLTemplate.optimize(XSLTemplate.java:611)
        at net.sf.saxon.style.StylesheetPackage.optimizeTopLevel(StylesheetPackage.java:1216)
        at net.sf.saxon.style.StylesheetPackage.compile(StylesheetPackage.java:1083)
        at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:201)
        at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:94)
        at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:543)
        at net.sf.saxon.Transform.doTransform(Transform.java:601)
        at net.sf.saxon.Transform.main(Transform.java:80)
Fatal error during transformation: java.lang.ClassCastException: net.sf.saxon.expr.GeneralComparison10 cannot be cast to net.sf.saxon.expr.ComparisonExpression

test.xsl:


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:sch="http://purl.oclc.org/dsdl/schematron"
    xmlns:loc="http://www.thaiopensource.com/ns/location"
    xmlns:err="http://www.thaiopensource.com/ns/error"
    xmlns:xsltc="http://www.thaiopensource.com/ns/xsltc"
    xmlns:jing-extension-functions="http://www.thaiopensource.com/ns/extension-functions"
    xmlns:osaxon="http://icl.com/saxon"
    xmlns:nsaxon="http://saxon.sf.net/"
    xmlns:xalan-node-info="http://xml.apache.org/xalan/java/org.apache.xalan.lib.NodeInfo"
    version="1.0">
    <xsl:template match="/">
        <result>
            <xsl:apply-templates select="/" mode="all"/>
        </result>
    </xsl:template>
    <xsl:variable name="id-set" select="//*[@id]"/>
    <xsl:template match="*[@id]"
        mode="M1"
        priority="2"
        name="R1.1"
        loc:column-number="29"
        loc:line-number="6"
        loc:system-id="jar:file:/D:/workspace/eXml/lib/zip/epub/epubcheck-3.0.1.jar!/com/adobe/epubcheck/schema/20/sch/id-unique.sch">
        <xsl:if test="not(count($id-set[@id = current()/@id]) = 1)"
            loc:column-number="15"
            loc:line-number="8"
            loc:system-id="jar:file:/D:/workspace/eXml/lib/zip/epub/epubcheck-3.0.1.jar!/com/adobe/epubcheck/schema/20/sch/id-unique.sch">
            <failed-assertion test="count($id-set[@id = current()/@id]) = 1">
                <xsl:call-template name="location"/>
                <statement>Duplicate ID '<xsl:value-of select="current()/@id"
                    loc:column-number="63"
                    loc:line-number="8"
                    loc:system-id="jar:file:/D:/workspace/eXml/lib/zip/epub/epubcheck-3.0.1.jar!/com/adobe/epubcheck/schema/20/sch/id-unique.sch"/>'</statement>
            </failed-assertion>
        </xsl:if>
    </xsl:template>
    <xsl:template match="*[@id]"
        mode="all"
        priority="2"
        loc:column-number="29"
        loc:line-number="6"
        loc:system-id="jar:file:/D:/workspace/eXml/lib/zip/epub/epubcheck-3.0.1.jar!/com/adobe/epubcheck/schema/20/sch/id-unique.sch">
        <xsl:call-template name="R1.1"/>
        <xsl:apply-templates select="*" mode="all"/>
    </xsl:template>
    <xsl:template match="*" mode="M1"/>
    <xsl:template match="*|/" mode="all">
        <xsl:apply-templates select="*" mode="all"/>
    </xsl:template>
    <xsl:template name="location">
        <xsl:attribute name="column-number">
            <xsl:value-of select="nsaxon:column-number()"/>
        </xsl:attribute>
        <xsl:attribute name="line-number">
            <xsl:value-of select="nsaxon:line-number()"/>
        </xsl:attribute>
        <xsl:attribute name="system-id">
            <xsl:value-of select="nsaxon:system-id()"/>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

test.xml


<?xml version="1.0" encoding="UTF-8"?>
<personnel/>
Actions #5

Updated by Michael Kay over 9 years ago

  • Status changed from Closed to In Progress

Indeed, there seems to be an interaction here with the fix for bug #2307.

Actions #6

Updated by Michael Kay over 9 years ago

What's happening in this case is the following sequence of events:

(a) The relevant expression is count($id-set[@id = current()/@id]) = 1

(b) Saxon establishes correctly that the inner expression $id-set[@id = current()/@id] can benefit from indexing, because the comparison is between two untypedAtomic values which is the same in 1.0 and 2.0 mode. So this subexpression gets rewritten as an IndexedFilterExpression

(c) Subsequently, Saxon establishes that the expression count(X) = 1 can be expressed as a simple ValueComparison since the types of both operands are known, and both are singletons.

(d) Having rewritten count(X) = 1 as a ValueComparison, Saxon invokes simplify() on the new ValueComparison. The invokes simplify() recursively on contained subexpressions. This establishes that the GeneralComparison [@id = current()/@id] is executed in 1.0 mode, and attempts therefore to revert it to an instance of GeneralComparison10; but an instance of GeneralComparison10 cannot be used in an IndexedFilterExpression and the attempt to do so causes the ClassCastException.

Removing the call on simplify() solves the problem, but it's difficult to very that it has no adverse side-effects. Generally speaking, simplify() should only be called on an expression immediately after it is constructed by the XPath parser, and should not be called again on expressions constructed during the typeCheck() or optimize() phases unless there is specific reason for it. So I think removing this call (at GeneralComparison.java line 314) is sound, but it needs testing.

Actions #7

Updated by Michael Kay over 9 years ago

  • Status changed from In Progress to Resolved
  • Fixed in version deleted (9.6.0.3)

I have run regression tests which indicate that this change (removing the call on simplify()) seems to be OK, so I am committing the change.

Actions #8

Updated by Michael Kay over 9 years ago

No change made on the 9.7 branch, which already omits the call to simplify().

Actions #9

Updated by O'Neil Delpratt over 9 years ago

  • Status changed from Resolved to Closed
  • Fixed in version set to 9.6.0.6

Bug fix applied in the Saxon 9.6.0.6 maintenance release.

Actions #10

Updated by O'Neil Delpratt almost 9 years ago

  • Sprint/Milestone set to 9.6.0.6
  • Applies to branch 9.6 added
  • Fix Committed on Branch 9.6 added
  • Fixed in Maintenance Release 9.6.0.6 added

Please register to edit this issue

Also available in: Atom PDF