This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 29482 - [XSLT30] Focus-changing constructs
Summary: [XSLT30] Focus-changing constructs
Status: RESOLVED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XSLT 3.0 (show other bugs)
Version: Candidate Recommendation
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-02-18 23:51 UTC by Michael Kay
Modified: 2016-11-11 10:48 UTC (History)
1 user (show)

See Also:


Attachments

Description Michael Kay 2016-02-18 23:51:14 UTC
Raising this as a test bug though it could migrate to a spec bug.

si-group-031, which originates from a Saxon bug report by Martin Honnen, does this within a streamable template:

        <xsl:for-each-group select="product" 
               group-adjacent="(position() - 1) idiv $block-size">
            <xsl:result-document href="product{current-grouping-key()}.xml">
                <xsl:copy select="$root">
                    <xsl:copy-of select="current-group()"/>
                </xsl:copy>
            </xsl:result-document>
        </xsl:for-each-group>

Saxon assesses this as non-streamable, and I think the analysis can be justified as follows:

19.8.9.4 says current-group() is not streamable if the path in the construct tree that connects it to the sequence constructor forming the body of the for-each-group instruction is such that some child construct is a higher-order operand of its parent

But the sequence constructor within xsl:copy[@select] is a higher-order operand of the xsl:copy instruction, because the context item for evaluation of O is different from the context item for evaluation of C.
Comment 1 Abel Braaksma 2016-02-20 14:47:14 UTC
I agree. Martin Honnen created a similar bug report against Exselt (we have different results and consider it streamable, but output is empty for the copied streamed nodes).

An alternative way of writing this, considering only copies of nodes are created, is the following, which I believe should be streamable (not pretty, but works):

<xsl:for-each-group select="product" 
      group-adjacent="(position() - 1) idiv $block-size">
   <xsl:result-document href="product{current-grouping-key()}.xml">
       <xsl:variable name="copy" select="current-group()/copy-of()" />
       <xsl:copy select="$root">
           <xsl:copy-of select="$copy"/>
       </xsl:copy>
   </xsl:result-document>
</xsl:for-each-group>

Note the necessity for adding the copy-of function to the variable @select expression.
Comment 2 Abel Braaksma 2016-11-03 14:01:59 UTC
I am turning this into a spec bug. This is what we say on higher-order operands:

[Definition: Whether or not the operand is higher-order. For this purpose an operand O of a construct C is higher-order if the semantics of C potentially require O to be evaluated more than once during a single evaluation of C.]

The part that doesn't fit with both our analysis is:

"potentially require O to be evaluated more than once during a single evaluation of C."

This rule has two side effects that we may not have anticipated:

1) if the focus-changing construct changes to a grounded node
2) if the evaluation does not *require* multiple evaluations of O

Example situation 1) (same as this issue)

<xsl:for-each-group 
    select="product" 
    group-adjacent="(position() - 1) idiv $block-size">

    <xsl:result-document href="product{current-grouping-key()}.xml">
        <xsl:copy select="$root">

            <!-- this seqtor is NOT higher-order, the node is grounded -->

            <!-- hence, this is allowed: -->
            <xsl:copy-of select="current-group()"/>
        </xsl:copy>
    </xsl:result-document>

</xsl:for-each-group>

Example situation 2)

<xsl:for-each select="foo[1]">
   <xsl:value-of select="every $x in bar satisfies @zed" />
</xsl:for-each>

-----------

We may want to tighten the rule on higher-order constructs to prevent this, but we can also leave it as it is, because at least in theory, there is no bug with the spec, only with our interpretation (we could do with some examples in this section in the spec, there are none).
Comment 3 Abel Braaksma 2016-11-03 14:19:02 UTC
> every $x in bar satisfies @zed
Bad example, the rules say "the 'in' expression has usage 'navigation'". I'll think of something else.
Comment 4 Abel Braaksma 2016-11-03 15:47:11 UTC
A correct example is, for instance:

every $x in ITEM[1]/string(.)
satisfies current()/document-uri() ! ends-with (., $x)

Or, subtly different:

every $x in ITEM/string(.)
satisfies current()/document-uri() ! ends-with (., $x)

The second one, with the current rules, fails ("more than one evaluation of current()"). The first one, with the current rules, succeeds ("exactly one evaluation of current()").

Though one could argue that both are streamable because the expression in the satisfied-clause is motionless, but the rules on focus-changing constructs disallow it.
Comment 5 Michael Kay 2016-11-03 17:40:20 UTC
The definition of higher-order operand is effectively:

[An operand O of a construct C is higher-order if the semantics of C potentially require O to be evaluated more than once during a single evaluation of C.] More specifically, O is a higher-order operand of C if any of the following conditions is true:

(1) The context item for evaluation of O is different from the context item for evaluation of C.

(2) C is an instruction and O is a pattern (as with the from and count attributes of xsl:number, and the group-starting-with and group-ending-with attributes of xsl:for-each-group).

(3) C is an XPath for, some, or every expression and O is the expression in its return or satisfies clause.

(4) C is an inline function declaration and O is the expression in its body.

I think the "]" here is misplaced (we used to have markup restrictions that prevented multi-paragraph definitions). The real test as to whether something is higher order is the conditions (1) to (4).

In this example, the body of xsl:copy satisfies condition (1) even though it is only evaluated once.

So if we want to make this example streamable then we have to change the rules, and the question is how we should change them.

I think my preference is NOT to make this guaranteed streamable: (a) it complicates the spec; (b) complicates my implementation; (c) I think it's not easy to convince ourselves that we aren't opening up lots of unintended consequences if we make a change here.

As regards the examples in comment #4, my reading of the spec is that the "more specifically" rules take precedence; the rule "C is an XPath for, some, or every expression and O is the expression in its return or satisfies clause." applies whether or not the controlling expression can actually deliver multiple items.
Comment 6 Abel Braaksma 2016-11-05 06:15:51 UTC
I agree in principle with comment#5, while I welcome widening the rules on streaming where and when it makes sense, this is already a complex area of the streamability rules and widening the rules does not make it simpler.

And of course, we should allow some room for implementations to improve on the limits the spec has :).
Comment 7 Michael Kay 2016-11-11 10:48:29 UTC
The Working Group decided that although this test case was potentially streamable, the streamability rules would not be changed to make it guaranteed streamable.

I have added two notes to the spec, one under "higher order operand" and one under "streamability of xsl:copy" to explain this restriction and to describe the workaround.

I have changed the expected result of the test case.