James Xu created SPARK-60934:
--------------------------------

             Summary: [SQL] Treat seedless TABLESAMPLE and aes_encrypt without 
an IV as non-deterministic
                 Key: SPARK-60934
                 URL: https://issues.apache.org/jira/browse/SPARK-60934
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 5.0.0
            Reporter: James Xu


h2. Problem

Two operators draw fresh randomness at execution but report {{deterministic = 
true}}. Rules that trust the flag then compute the operator once and reuse the 
result, so two references change from two independent draws to one shared draw.

*Scenario 1: seedless TABLESAMPLE.*

{{TABLESAMPLE}} without {{REPEATABLE}} parses to {{Sample(seed = None)}}. 
{{Sample}} carries no expressions, so {{QueryPlan.deterministic}} only checks 
the child and returns true. {{SampleExec}} draws the seed in its constructor 
through {{Sample.resolveSeed}}, once per physical operator.

Measured on a 20000-row table, with AQE and broadcast off:

{code:sql}
CREATE VIEW vs AS SELECT id FROM t TABLESAMPLE (50 PERCENT);
SELECT count(DISTINCT id), count(*)
FROM (SELECT id FROM vs UNION ALL SELECT id FROM vs);
{code}

Independent references return about 15000 distinct ids over about 20000 rows 
({{[14970, 19864]}}, {{[15047, 20052]}}). One shared sample returns about 10000 
distinct ids ({{[9998, 19996]}}, {{[10060, 20120]}}). The same view with 
{{REPEATABLE (7)}} returns {{[10005, 20010]}} either way, because the seed is 
already fixed on the operator.

*Scenario 2: aes_encrypt without an IV.*

{{aes_encrypt(expr, key)}} defaults to GCM and an empty IV. {{AesEncrypt}} does 
not override {{deterministic}}, and its children are ordinary expressions, so 
the flag is true. {{ExpressionImplUtils.aesEncrypt}} calls {{SecureRandom}} 
when the IV is null or empty and prepends that IV to the ciphertext.

{code:sql}
CREATE VIEW v AS SELECT aes_encrypt(payload, key) AS c FROM t;
SELECT count(DISTINCT c), count(*)
FROM (SELECT c FROM v UNION ALL SELECT c FROM v);
{code}

Independent references produce two ciphertexts per input row. A shared plan 
produces one.

h2. Root cause

{{QueryPlan.deterministic}} is "every expression and every child is 
deterministic". Randomness that is not an expression is invisible to that walk.

* {{Sample}} stores the seed as {{Option[Long]}}, not an expression. {{None}} 
means "draw at execution". {{rand}}, {{randn}}, {{uuid}}, and {{shuffle}} do 
not have this hole: they extend {{Nondeterministic}}, and {{ResolveRandomSeed}} 
writes a concrete seed during analysis.
* {{AesEncrypt}} is a {{RuntimeReplaceable}} whose replacement is a 
{{StaticInvoke}} of {{aesEncrypt}}. The random IV is inside that Java method. 
{{ReplaceExpressions}} runs before {{InlineCTE}} and substitutes the 
replacement, so an override on {{AesEncrypt}} alone does not reach later rules 
unless the {{StaticInvoke}} is also non-deterministic.

Call sites that reuse a plan when the flag is true include {{InlineCTE}} (a 
multi-reference CTE is inlined when {{cteDef.deterministic}} is true), 
scalar-subquery merging, SQL-function determinism, and view-to-CTE conversion. 
A special case at one call site leaves the others, and a {{Sample}}-only check 
still accepts {{aes_encrypt}}.

h2. Proposed fix

Report the operator non-deterministic at the source, in a dedicated change 
rather than as a special case at one call site.

* {{Sample.deterministic}} is {{seed.isDefined && child.deterministic}}. 
{{REPEATABLE}} and a programmatic seed stay deterministic, so sharing them does 
not change rows.
* {{AesEncrypt}} is non-deterministic when the IV is generated, and its 
{{StaticInvoke}} replacement passes {{isDeterministic = false}} in that case. 
An explicit IV stays deterministic.

Existing {{deterministic}} checks then keep seedless samples and generated-IV 
encryptions from being collapsed. A multi-reference CTE containing either 
evaluates once, matching {{rand()}}.




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

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

Reply via email to