Mark Jarvin created SPARK-60267:
-----------------------------------

             Summary: Segment-tree window frame fails with negative 
maxCachedBlocks for ROWS offsets near Int.MaxValue
                 Key: SPARK-60267
                 URL: https://issues.apache.org/jira/browse/SPARK-60267
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 5.0.0
            Reporter: Mark Jarvin


h2. Summary

With {{spark.sql.windowExec.segmentTree.enabled=true}} (default {{false}}, 
added in 4.2.0), a ROWS window frame with an offset near {{Int.MaxValue}} fails 
the query with:

{noformat}
java.lang.IllegalArgumentException: requirement failed: maxCachedBlocks must be 
>= 1 when specified, got -32766
  at 
org.apache.spark.sql.execution.window.WindowSegmentTree.<init>(WindowSegmentTree.scala:69)
  at 
org.apache.spark.sql.execution.window.SegmentTreeWindowFunctionFrame.prepare(SegmentTreeWindowFunctionFrame.scala:165)
{noformat}

No large data is needed; five rows reproduce it.

h2. Reproduction

{code:sql}
SET spark.sql.windowExec.segmentTree.enabled=true;
SET spark.sql.windowExec.segmentTree.minPartitionRows=1;
SELECT id, sum(id) OVER (ORDER BY id ROWS BETWEEN CURRENT ROW AND 2147483647 
FOLLOWING)
FROM VALUES (1), (2), (3), (4), (5) AS t(id);
{code}

Frames that fail with the segment tree enabled:
* {{ROWS BETWEEN CURRENT ROW AND 2147483647 FOLLOWING}} (got -32766)
* {{ROWS BETWEEN 2147483647 PRECEDING AND CURRENT ROW}} (got -32766)
* {{ROWS BETWEEN 1 PRECEDING AND 2147483647 FOLLOWING}} (got -32765)
* {{ROWS BETWEEN 2147483647 PRECEDING AND 1 FOLLOWING}} (got -32765)

Frames that work: {{2147483647 PRECEDING AND 2147483647 FOLLOWING}}, and any 
frame with an UNBOUNDED bound. With the segment tree disabled (the default) all 
of these return correct results.

h2. Root cause

{{WindowEvaluatorFactoryBase.estimateMaxCachedBlocks}} computes the frame width 
in 32-bit {{Int}} arithmetic:

{code:scala}
case (CurrentRow, IntegerLiteral(hi)) => Some(math.abs(hi) + 1)
case (IntegerLiteral(lo), CurrentRow) => Some(math.abs(lo) + 1)
case (IntegerLiteral(lo), IntegerLiteral(hi)) => Some(math.abs(hi - lo) + 1)
{code}

For {{hi = Int.MaxValue}} the {{+ 1}} wraps to {{Int.MinValue}}, so {{ceil(w / 
blockSize) + 2}} is negative and the {{require}} in {{WindowSegmentTree}} 
fails. {{hi - lo}} can wrap the same way: for {{-2147483647 .. 2147483647}} it 
yields -2, so the budget is silently underestimated (a performance problem 
only).

h2. Proposed fix

Compute the width in {{Long}}, clamp the resulting block count to the {{Int}} 
range, and add a regression test that runs these frames with the segment tree 
enabled and compares against the non-segment-tree result. No existing suite 
covers huge ROWS offsets with the segment tree on.

The failure is loud (an exception), not a silent wrong result, and the feature 
is opt-in.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to