Project

Profile

Help

Bug #6660

open

Problem with `xsl:number level="any"` when count pattern uses `current()`

Added by Michael Kay 1 day ago. Updated 1 day ago.

Status:
New
Priority:
Low
Assignee:
Category:
-
Sprint/Milestone:
-
Start date:
2025-01-20
Due date:
% Done:

0%

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

Description

I tried to use

<xsl:template match="g:prodrecap">
   <xsl:number level="any" count="g:prodrecap[@ref = current()/@ref]"/>

and it gave strange results.

Working to produce a repro.

Actions #1

Updated by Michael Kay 1 day ago

Repro constructied in mike/bugs/2025/6660-Kay.

Running with 12.x, command line

Transform -xsl:assemble-spec.xsl -s:xquery-preprocessed.xml -t -o:out.xml

At assemble-spec#165 I am doing

     <xsl:variable name="duplicate" as="xs:integer" 
          select="xs:integer(count(preceding::prodrecap[@ref=current()/@ref]) + 1)"/>
    <xsl:variable name="duplicate2" as="xs:integer">
      <xsl:number level="any" count="prodrecap[@ref=current()/@ref]"/>
    </xsl:variable>
    <xsl:message expand-text="1">*** Duplicate {$duplicate} vs {$duplicate2} at {path(.)} ***</xsl:message>

I would expect the two variables to have the same value, but this is not the case.

The -explain output shows

                          <nodeNum role="value" level="any">
                              <dot role="select" type="1NE nQ{}prodrecap" flags="a"/>
                              <p.withCurrent role="count">
                                <p.withPredicate>
                                  <p.nodeTest test="NE nQ{}prodrecap"/>
                                  <gc10 op="="
                                        cardinality="many-to-many (1.0)"
                                        comp="GAC|http://www.w3.org/2005/xpath-functions/collation/codepoint">
                                    <axis name="attribute" nodeTest="NA nQ{}ref"/>
                                    <docOrder intra="1">
                                      <slash>
                                        <varRef name="Q{http://www.w3.org/2005/xpath-functions}current" slot="3"/>
                                        <axis name="attribute" nodeTest="NA nQ{}ref"/>
                                      </slash>
                                    </docOrder>
                                  </gc10>
                                </p.withPredicate>
                              </p.withCurrent>
                            </nodeNum>

which looks reasonable -- except that it made me realise there is code running in 1.0 compatibility mode.

Changing this doesn't materially affect the result - the explain output now shows a <vc> instruction in place of a <gc10>.

Actions #2

Updated by Michael Kay 1 day ago

Navigator.getNumberAny() is being called with the argument hasVariablesInPatterns set to false, which means that the function uses memoization, which means that it works by search back to the last numbered/matching node and adding 1 to the number. This optimization is inappropriate when the pattern has a reference to current().

Actions #3

Updated by Michael Kay 1 day ago

I think this is a user error.

In the construct

<xsl:number level="any" count="g:prodrecap[@ref = current()/@ref]"/>

The function current() does not return the node being numbered, it returns the node being matched against the count() pattern, which is a different node each time. The spec says:

If the current function is used within a pattern, its value is the item that is being matched against the pattern.

Changing the code to

      <xsl:variable name="this" select="."/>
      <xsl:number level="any" count="prodrecap[@ref=$this/@ref]"/>

However, it's not optimised so it has quadratic performance -- and gives no benefit over count(preceding::prodrecap[....]) A more efficient solution would probably be to compute the numbers while the nodes are being generated in the previous pass.

Please register to edit this issue

Also available in: Atom PDF