This is an automated email from the ASF dual-hosted git repository.

dongjoon-hyun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/spark-connect-swift.git


The following commit(s) were added to refs/heads/main by this push:
     new 1187c9b  [SPARK-60055] Fix `dropDuplicatesWithinWatermark` to set 
`withinWatermark` in `Deduplicate` relation
1187c9b is described below

commit 1187c9b6bacdd8db07fc7918d5c9e8d19576d017
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Wed Oct 7 14:43:55 2026 -0700

    [SPARK-60055] Fix `dropDuplicatesWithinWatermark` to set `withinWatermark` 
in `Deduplicate` relation
    
    ### What changes were proposed in this pull request?
    
    This PR aims to fix `SparkConnectClient.getDropDuplicates` to set the 
`within_watermark` field of the `Deduplicate` relation by adding 
`deduplicate.withinWatermark = withinWatermark`.
    
    ### Why are the changes needed?
    
    Previously, `getDropDuplicates` accepted the `withinWatermark` parameter 
but never propagated it to `Spark_Connect_Deduplicate`. As a result, the public 
`DataFrame.dropDuplicatesWithinWatermark(...)` API silently behaved like 
`dropDuplicates(...)`. For streaming queries, this gives the wrong semantics: 
the server keeps unbounded deduplication state instead of the watermark-bounded 
state of `DeduplicateWithinWatermark`.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes, this is a bug fix. `dropDuplicatesWithinWatermark` now sends 
`within_watermark = true` to the Spark Connect server, consistent with the 
Scala and PySpark Spark Connect clients. As a result, calling it on a batch 
`DataFrame` now fails with `dropDuplicatesWithinWatermark is not supported with 
batch DataFrames/DataSets`, the same as other Spark clients. Previously, it 
silently behaved like `dropDuplicates`.
    
    ### How was this patch tested?
    
    Pass the CIs with the newly added plan-level test case, 
`DataFrameInternalTests.dropDuplicatesWithinWatermarkPlan`, which does not 
require a Spark Connect server. It fails without this fix and passes with it.
    
    ```
    $ swift test --no-parallel --filter 
DataFrameInternalTests/dropDuplicatesWithinWatermarkPlan
    ```
    
    In addition, the existing `DataFrameTests.dropDuplicatesWithinWatermark` 
and `DataFrameTests.withWatermark` test cases are updated to expect an error 
for batch `DataFrame`s.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 5.5
    
    Closes #586 from dongjoon-hyun/SPARK-60055.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 Sources/SparkConnect/SparkConnectClient.swift        |  1 +
 Tests/SparkConnectTests/DataFrameInternalTests.swift | 13 +++++++++++++
 Tests/SparkConnectTests/DataFrameTests.swift         | 13 ++++++++++---
 3 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/Sources/SparkConnect/SparkConnectClient.swift 
b/Sources/SparkConnect/SparkConnectClient.swift
index db28eb9..358f394 100644
--- a/Sources/SparkConnect/SparkConnectClient.swift
+++ b/Sources/SparkConnect/SparkConnectClient.swift
@@ -709,6 +709,7 @@ public actor SparkConnectClient {
     } else {
       deduplicate.columnNames = columnNames
     }
+    deduplicate.withinWatermark = withinWatermark
     return createPlan { $0.deduplicate = deduplicate }
   }
 
diff --git a/Tests/SparkConnectTests/DataFrameInternalTests.swift 
b/Tests/SparkConnectTests/DataFrameInternalTests.swift
index 0f84c48..c686c7c 100644
--- a/Tests/SparkConnectTests/DataFrameInternalTests.swift
+++ b/Tests/SparkConnectTests/DataFrameInternalTests.swift
@@ -165,4 +165,17 @@ struct DataFrameInternalTests {
     }
     await spark.stop()
   }
+
+  @Test
+  func dropDuplicatesWithinWatermarkPlan() async throws {
+    // This test doesn't require a running server because it only inspects the 
plan.
+    let spark = try SparkSession("sc://localhost")
+    var plan = Plan()
+    plan.root.range.end = 10
+    let df = DataFrame(spark: spark, plan: plan)
+    #expect(await df.dropDuplicates().plan.root.deduplicate.withinWatermark == 
false)
+    #expect(await 
df.dropDuplicates("id").plan.root.deduplicate.withinWatermark == false)
+    #expect(await 
df.dropDuplicatesWithinWatermark().plan.root.deduplicate.withinWatermark)
+    #expect(await 
df.dropDuplicatesWithinWatermark("id").plan.root.deduplicate.withinWatermark)
+  }
 }
diff --git a/Tests/SparkConnectTests/DataFrameTests.swift 
b/Tests/SparkConnectTests/DataFrameTests.swift
index 5889470..f30fc8b 100644
--- a/Tests/SparkConnectTests/DataFrameTests.swift
+++ b/Tests/SparkConnectTests/DataFrameTests.swift
@@ -1312,8 +1312,13 @@ struct DataFrameTests {
   func dropDuplicatesWithinWatermark() async throws {
     let spark = try await SparkSession.builder.getOrCreate()
     let df = try await spark.sql("SELECT * FROM VALUES (1), (2), (3), (1), (3) 
T(a)")
-    #expect(try await df.dropDuplicatesWithinWatermark().count() == 3)
-    #expect(try await df.dropDuplicatesWithinWatermark("a").count() == 3)
+    // Spark doesn't support `dropDuplicatesWithinWatermark` with batch 
DataFrames.
+    try await #require(throws: Error.self) {
+      try await df.dropDuplicatesWithinWatermark().count()
+    }
+    try await #require(throws: Error.self) {
+      try await df.dropDuplicatesWithinWatermark("a").count()
+    }
     await spark.stop()
   }
 
@@ -1332,7 +1337,9 @@ struct DataFrameTests {
         """
       )
       .withWatermark("eventTime", "1 minute")  // This tests only API for now
-    #expect(try await df.dropDuplicatesWithinWatermark("data").count() == 1)
+    try await #require(throws: Error.self) {
+      try await df.dropDuplicatesWithinWatermark("data").count()
+    }
     await spark.stop()
   }
 


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

Reply via email to