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 e703d2f  [SPARK-60051] Fix `DataFrame.collect()` to return `Data` for 
`BINARY` columns instead of crashing
e703d2f is described below

commit e703d2f8aa3685565970009238a8ac4fb1678857
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Wed Oct 7 13:11:52 2026 -0700

    [SPARK-60051] Fix `DataFrame.collect()` to return `Data` for `BINARY` 
columns instead of crashing
    
    ### What changes were proposed in this pull request?
    
    This PR fixes `DataFrame.collect()` to read `BINARY` column values directly 
from `BinaryArray` as `Data`, instead of going through `BinaryArray.asString` 
and returning a `String.UTF8View`. `Row` equality now also compares `Data` 
values.
    
    ### Why are the changes needed?
    
    `BinaryArray.asString` force-unwraps `String(data:encoding: .utf8)`, so 
collecting any binary value that is not valid UTF-8 (e.g. `X'FF00FE'`) crashes 
the process. Returning `Data` also matches PySpark, which returns `bytearray`.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes. `BINARY` values in collected rows are now `Data` instead of 
`String.UTF8View`, and non-UTF-8 binary values no longer crash.
    
    ### How was this patch tested?
    
    Pass the CIs with the newly added test cases.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Sonnet 5.5
    
    Closes #583 from dongjoon-hyun/claude/fix-binary-collect.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 Sources/SparkConnect/DataFrame+Actions.swift       | 4 ++--
 Sources/SparkConnect/Row.swift                     | 2 ++
 Tests/SparkConnectTests/CreateDataFrameTests.swift | 1 +
 Tests/SparkConnectTests/DataFrameTests.swift       | 9 +++++++++
 4 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/Sources/SparkConnect/DataFrame+Actions.swift 
b/Sources/SparkConnect/DataFrame+Actions.swift
index 414b237..835d1d3 100644
--- a/Sources/SparkConnect/DataFrame+Actions.swift
+++ b/Sources/SparkConnect/DataFrame+Actions.swift
@@ -172,11 +172,11 @@ extension DataFrame {
                 values.append(TimestampNanos(epochNanos: timestamp))
               }
             case ArrowType.ArrowBinary:
-              guard let binaryArray = array as? AsString else {
+              guard let binaryArray = array as? BinaryArray else {
                 throw SparkConnectError.invalidArrowData(
                   SparkConnectError.Details(message: "Invalid Arrow `binary` 
column."))
               }
-              values.append(binaryArray.asString(i).utf8)
+              values.append(binaryArray[i])
             case .complexInfo(.strct):
               guard let structArray = array as? AsString else {
                 throw SparkConnectError.invalidArrowData(
diff --git a/Sources/SparkConnect/Row.swift b/Sources/SparkConnect/Row.swift
index 35f6ad1..61db2bf 100644
--- a/Sources/SparkConnect/Row.swift
+++ b/Sources/SparkConnect/Row.swift
@@ -435,6 +435,8 @@ public struct Row: Sendable, Equatable {
         return a == b
       } else if let a = x as? String, let b = y as? String {
         return a == b
+      } else if let a = x as? Data, let b = y as? Data {
+        return a == b
       } else if let a = x as? [Bool], let b = y as? [Bool] {
         return a == b
       } else if let a = x as? [any FixedWidthInteger], let b = y as? [any 
FixedWidthInteger] {
diff --git a/Tests/SparkConnectTests/CreateDataFrameTests.swift 
b/Tests/SparkConnectTests/CreateDataFrameTests.swift
index 15f73c5..55b0a29 100644
--- a/Tests/SparkConnectTests/CreateDataFrameTests.swift
+++ b/Tests/SparkConnectTests/CreateDataFrameTests.swift
@@ -151,6 +151,7 @@ struct CreateDataFrameTests {
     let spark = try await SparkSession.builder.getOrCreate()
     let df = try await spark.createDataFrame([[Data([1, 2, 3])], [nil]], "a 
BINARY")
     #expect(try await df.count() == 2)
+    #expect(try await df.collect() == [Row(Data([1, 2, 3])), Row(nil)])
     await spark.stop()
   }
 
diff --git a/Tests/SparkConnectTests/DataFrameTests.swift 
b/Tests/SparkConnectTests/DataFrameTests.swift
index 64a4efc..5889470 100644
--- a/Tests/SparkConnectTests/DataFrameTests.swift
+++ b/Tests/SparkConnectTests/DataFrameTests.swift
@@ -380,6 +380,15 @@ struct DataFrameTests {
     await spark.stop()
   }
 
+  @Test
+  func collectBinary() async throws {
+    let spark = try await SparkSession.builder.getOrCreate()
+    let rows = try await spark.sql("SELECT X'FF00FE' AS b, CAST(NULL AS 
BINARY) AS n").collect()
+    #expect(rows == [Row(Data([0xFF, 0x00, 0xFE]), nil)])
+    #expect(try rows[0].get(0) as? Data == Data([0xFF, 0x00, 0xFE]))
+    await spark.stop()
+  }
+
   @Test
   func selectNone() async throws {
     let spark = try await SparkSession.builder.getOrCreate()


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

Reply via email to