[Bug 30060] New: [XSLT30]xsl:iterate example "Collecting Multiple Values in a Single Pass" not working

https://www.w3.org/Bugs/Public/show_bug.cgi?id=30060

            Bug ID: 30060
           Summary: [XSLT30]xsl:iterate example "Collecting Multiple
                    Values in a Single Pass" not working
           Product: XPath / XQuery / XSLT
           Version: Member-only Editors Drafts
          Hardware: PC
                OS: Windows NT
            Status: NEW
          Severity: editorial
          Priority: P2
         Component: XSLT 3.0
          Assignee: mike@saxonica.com
          Reporter: martin.honnen@gmx.de
        QA Contact: public-qt-comments@w3.org
  Target Milestone: ---

Today I tried to run the example "Collecting Multiple Values in a Single Pass"
in https://www.w3.org/XML/Group/qtspecs/specifications/xslt-30/html/#iterate,
it has the source code

<xsl:source-document streamable="yes" href="employees.xml">
  <xsl:iterate select="employees/employee">
    <xsl:param name="highest" as="element(employee)*"/>
    <xsl:param name="lowest" as="element(employee)*"/>
    <xsl:on-completion>
      <highest-paid-employees>
        <xsl:value-of select="$highest/name"/>
      </highest-paid-employees>
      <lowest-paid-employees>
        <xsl:value-of select="$lowest/name"/>
      </lowest-paid-employees>  
    </xsl:on-completion>   
    <xsl:variable name="is-new-highest" as="xs:boolean"
                  select="empty($highest[@salary ge current()/@salary])"/>
    <xsl:variable name="is-equal-highest" as="xs:boolean" 
                  select="exists($highest[@salary eq current()/@salary])"/> 
    <xsl:variable name="is-new-lowest" as="xs:boolean" 
                  select="empty($lowest[@salary le current()/@salary])"/>
    <xsl:variable name="is-equal-lowest" as="xs:boolean" 
                  select="exists($lowest[@salary eq current()/@salary])"/> 
    <xsl:variable name="new-highest-set" as="element(employee)*"
                  select="if ($is-new-highest) then .
                          else if ($is-equal-highest) then ($highest, .)
                          else $highest"/>
    <xsl:variable name="new-lowest-set" as="element(employee)*"
                  select="if ($is-new-lowest) then .
                          else if ($is-equal-lowest) then ($lowest, .)
                          else $lowest"/>
    <xsl:next-iteration>
      <xsl:with-param name="highest" select="$new-highest-set"/>
      <xsl:with-param name="lowest" select="$new-lowest-set"/>
    </xsl:next-iteration>
   </xsl:iterate>
 </xsl:source-document>

Reading it I wondered whether it is possible to pass on streamed nodes on as
parameters and furthermore whether it would be possible to use "." at several
places in the body of the xsl:iterate.

So I constructed a sample input document 

<employees>
        <employee salary="3000">
                <name>Employee a</name>
        </employee>
        <employee salary="1000">
                <name>Employee b</name>
        </employee>
        <employee salary="3000">
                <name>Employee c</name>
        </employee>
        <employee salary="2000">
                <name>Employee d</name>
        </employee>
</employees>

and a complete stylesheet with 

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
        version="3.0">

        <xsl:template name="main">
                <xsl:source-document streamable="yes"
href="test2017020702.xml">
                        <xsl:iterate select="employees/employee">
                                <xsl:param name="highest"
as="element(employee)*"/>
                                <xsl:param name="lowest"
as="element(employee)*"/>
                                <xsl:on-completion>
                                        <highest-paid-employees>
                                                <xsl:value-of
select="$highest/name"/>
                                        </highest-paid-employees>
                                        <lowest-paid-employees>
                                                <xsl:value-of
select="$lowest/name"/>
                                        </lowest-paid-employees>
                                </xsl:on-completion>
                                <xsl:variable name="is-new-highest"
as="xs:boolean"
                                        select="empty($highest[@salary ge
current()/@salary])"/>
                                <xsl:variable name="is-equal-highest"
as="xs:boolean"
                                        select="exists($highest[@salary eq
current()/@salary])"/>
                                <xsl:variable name="is-new-lowest"
as="xs:boolean"
                                        select="empty($lowest[@salary le
current()/@salary])"/>
                                <xsl:variable name="is-equal-lowest"
as="xs:boolean"
                                        select="exists($lowest[@salary eq
current()/@salary])"/>
                                <xsl:variable name="new-highest-set"
as="element(employee)*"
                                        select="
                                                if ($is-new-highest) then
                                                        .
                                                else
                                                        if ($is-equal-highest)
then
                                                                ($highest, .)
                                                        else
                                                                $highest"/>
                                <xsl:variable name="new-lowest-set"
as="element(employee)*"
                                        select="
                                                if ($is-new-lowest) then
                                                        .
                                                else
                                                        if ($is-equal-lowest)
then
                                                                ($lowest, .)
                                                        else
                                                                $lowest"/>
                                <xsl:next-iteration>
                                        <xsl:with-param name="highest"
select="$new-highest-set"/>
                                        <xsl:with-param name="lowest"
select="$new-lowest-set"/>
                                </xsl:next-iteration>
                        </xsl:iterate>
                </xsl:source-document>
        </xsl:template>

</xsl:stylesheet>

however when I try to run that with Saxon-EE 9.7.0.14J it refuses to run it,
telling me 

Static error on line 8 column 67 of test2017020702.xsl:
  XTSE3430: The body of the xsl:stream instruction is not streamable
  *  Operand if($is-new-highest) then ... else ... of let $new-highest-set :=
... selects
  streamed nodes in a context that allows arbitrary navigation (line 36)


I looked into the test cases of the test suite whether it has some input/xslt
sample similar to that spec example but I couldn't find anything. 

So I tried fixing the example based on what I have learned in the past trying
to get code working as streamable with Saxon 9.7 and I had to make sure I
construct a copy-of() first of the context node in a variable and use that copy
then in all occasions:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
        version="3.0">

        <xsl:output indent="yes"/>

        <xsl:template name="main">
                <xsl:source-document streamable="yes"
href="test2017020702.xml">
                        <xsl:iterate select="employees/employee">
                                <xsl:param name="highest"
as="element(employee)*"/>
                                <xsl:param name="lowest"
as="element(employee)*"/>
                                <xsl:on-completion>
                                        <highest-paid-employees>
                                                <xsl:value-of
select="$highest/name" separator=","/>
                                        </highest-paid-employees>
                                        <lowest-paid-employees>
                                                <xsl:value-of
select="$lowest/name" separator=","/>
                                        </lowest-paid-employees>
                                </xsl:on-completion>
                                <xsl:variable name="copy" select="copy-of()"/>
                                <xsl:variable name="is-new-highest"
as="xs:boolean"
                                        select="empty($highest[@salary ge
$copy/@salary])"/>
                                <xsl:variable name="is-equal-highest"
as="xs:boolean"
                                        select="exists($highest[@salary eq
$copy/@salary])"/>
                                <xsl:variable name="is-new-lowest"
as="xs:boolean"
                                        select="empty($lowest[@salary le
$copy/@salary])"/>
                                <xsl:variable name="is-equal-lowest"
as="xs:boolean"
                                        select="exists($lowest[@salary eq
$copy/@salary])"/>
                                <xsl:variable name="new-highest-set"
as="element(employee)*"
                                        select="
                                        if ($is-new-highest) then
                                        $copy
                                        else
                                        if ($is-equal-highest) then
                                        ($highest, $copy)
                                        else
                                        $highest"/>
                                <xsl:variable name="new-lowest-set"
as="element(employee)*"
                                        select="
                                        if ($is-new-lowest) then
                                        $copy
                                        else
                                        if ($is-equal-lowest) then
                                        ($lowest, $copy)
                                        else
                                        $lowest"/>
                                <xsl:next-iteration>
                                        <xsl:with-param name="highest"
select="$new-highest-set"/>
                                        <xsl:with-param name="lowest"
select="$new-lowest-set"/>
                                </xsl:next-iteration>
                        </xsl:iterate>
                </xsl:source-document>
        </xsl:template>

</xsl:stylesheet>

I am not sure that rewrite is the intended implementation of that example but I
think the example in the spec should be fixed to work with an implementation
like Saxon.

-- 
You are receiving this mail because:
You are the QA Contact for the bug.

Received on Tuesday, 7 February 2017 11:52:57 UTC