This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.
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.
Thank you for pointing this out. The WG reviewed the bug and agreed. You are correct that the example as written is not guaranteed streamable (this emerges from the rules on streamability of xsl:variable, which can be paraphrased as saying that a streamed node cannot be bound to a variable), and that copying the context node fixes the problem. I've added it as a test case to the test suite (si-iterate-035/-036), and I have modified the example in the spec.