This is an automated email from the ASF dual-hosted git repository.
kou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new d14b42ac6b GH-35788: [Swift] bug fixes and change reader/writer to
user Result type (#35774)
d14b42ac6b is described below
commit d14b42ac6b25823042b9da83ee16615579a81fa1
Author: abandy <[email protected]>
AuthorDate: Sun May 28 03:09:19 2023 -0400
GH-35788: [Swift] bug fixes and change reader/writer to user Result type
(#35774)
Changes:
- Changed Reader and Writer to use Result<T, Error> instead of throwing.
Since the Error is an enum the handling of the error is enforced.
- Update RecordBatch to accept only a single array per column instead of
chunked arrays (which could cause problems when writing out)
- Added ability for Table to be created from RecordBatches
- Fixed a bug with indexing in Chunked array with many arrays
- Added tests for the above changes.
- Switched ValidationErrors to ArrowError and added additional ArrowError
values.
* Closes: #35788
Authored-by: Alva Bandy <[email protected]>
Signed-off-by: Sutou Kouhei <[email protected]>
---
swift/Arrow/Sources/Arrow/ArrowArray.swift | 65 +++++++-
swift/Arrow/Sources/Arrow/ArrowArrayBuilder.swift | 2 +-
swift/Arrow/Sources/Arrow/ArrowBufferBuilder.swift | 10 +-
swift/Arrow/Sources/Arrow/ArrowData.swift | 4 +-
swift/Arrow/Sources/Arrow/ArrowReader.swift | 144 ++++++++++-------
swift/Arrow/Sources/Arrow/ArrowReaderHelper.swift | 121 ++++++++++-----
swift/Arrow/Sources/Arrow/ArrowSchema.swift | 2 +
swift/Arrow/Sources/Arrow/ArrowTable.swift | 149 ++++++++----------
swift/Arrow/Sources/Arrow/ArrowType.swift | 12 +-
swift/Arrow/Sources/Arrow/ArrowWriter.swift | 172 ++++++++++++++-------
swift/Arrow/Sources/Arrow/ArrowWriterHelper.swift | 40 ++---
swift/Arrow/Sources/Arrow/ChunkedArray.swift | 71 ++++++++-
swift/Arrow/Tests/ArrowTests/IPCTests.swift | 138 +++++++++++------
.../Arrow/Tests/ArrowTests/RecordBatchTests.swift | 42 ++---
swift/Arrow/Tests/ArrowTests/TableTests.swift | 91 +++++++++--
15 files changed, 714 insertions(+), 349 deletions(-)
diff --git a/swift/Arrow/Sources/Arrow/ArrowArray.swift
b/swift/Arrow/Sources/Arrow/ArrowArray.swift
index 47c733502c..07a66dafe0 100644
--- a/swift/Arrow/Sources/Arrow/ArrowArray.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowArray.swift
@@ -17,7 +17,60 @@
import Foundation
-public class ArrowArray<T> {
+public class ArrowArrayHolder {
+ public let type: ArrowType.Info
+ public let length: UInt
+ public let nullCount: UInt
+ public let array: Any
+ public let getBufferData: () -> [Data]
+ public let getBufferDataSizes: () -> [Int]
+ private let getArrowColumn: (ArrowField, [ArrowArrayHolder]) throws ->
ArrowColumn
+ public init<T>(_ arrowArray: ArrowArray<T>) {
+ self.array = arrowArray
+ self.length = arrowArray.length
+ self.type = arrowArray.arrowData.type
+ self.nullCount = arrowArray.nullCount
+ self.getBufferData = {() -> [Data] in
+ var bufferData = [Data]()
+ for buffer in arrowArray.arrowData.buffers {
+ bufferData.append(Data())
+ buffer.append(to: &bufferData[bufferData.count - 1])
+ }
+
+ return bufferData;
+ }
+
+ self.getBufferDataSizes = {() -> [Int] in
+ var bufferDataSizes = [Int]()
+ for buffer in arrowArray.arrowData.buffers {
+ bufferDataSizes.append(Int(buffer.capacity))
+ }
+
+ return bufferDataSizes
+ }
+
+ self.getArrowColumn = {(field: ArrowField, arrayHolders:
[ArrowArrayHolder]) throws -> ArrowColumn in
+ var arrays = [ArrowArray<T>]()
+ for arrayHolder in arrayHolders {
+ if let array = arrayHolder.array as? ArrowArray<T> {
+ arrays.append(array)
+ }
+ }
+
+ return ArrowColumn(field, chunked: ChunkedArrayHolder(try
ChunkedArray<T>(arrays)))
+ }
+ }
+
+ public static func makeArrowColumn(_ field: ArrowField, holders:
[ArrowArrayHolder]) -> Result<ArrowColumn, ArrowError> {
+ do {
+ return .success(try holders[0].getArrowColumn(field, holders))
+ } catch {
+ return .failure(.runtimeError("\(error)"))
+ }
+ }
+}
+
+public class ArrowArray<T>: AsString {
public typealias ItemType = T
public let arrowData: ArrowData
public var nullCount : UInt {get{return self.arrowData.nullCount}}
@@ -29,7 +82,7 @@ public class ArrowArray<T> {
public func isNull(_ at: UInt) throws -> Bool {
if at >= self.length {
- throw ValidationError.outOfBounds(index: at)
+ throw ArrowError.outOfBounds(index: Int64(at))
}
return self.arrowData.isNull(at)
@@ -40,6 +93,14 @@ public class ArrowArray<T> {
fatalError("subscript() has not been implemented")
}
}
+
+ public func asString(_ index: UInt) -> String {
+ if self[index] == nil {
+ return ""
+ }
+
+ return "\(self[index]!)"
+ }
}
public class FixedArray<T>: ArrowArray<T> {
diff --git a/swift/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
b/swift/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
index 8672186061..c456f5a00b 100644
--- a/swift/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
@@ -107,7 +107,7 @@ public class ArrowArrayBuilders {
} else if t == Double.self {
return try NumberArrayBuilder<T>()
} else {
- throw ValidationError.unknownType
+ throw ArrowError.unknownType
}
}
diff --git a/swift/Arrow/Sources/Arrow/ArrowBufferBuilder.swift
b/swift/Arrow/Sources/Arrow/ArrowBufferBuilder.swift
index 6dc50208ed..587c55dab5 100644
--- a/swift/Arrow/Sources/Arrow/ArrowBufferBuilder.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowBufferBuilder.swift
@@ -39,7 +39,7 @@ public class BaseBufferBuilder<T> {
public var length: UInt = 0
public var nullCount : UInt = 0
- init(values: ArrowBuffer, nulls: ArrowBuffer, stride: Int =
MemoryLayout<T>.stride) throws {
+ init(values: ArrowBuffer, nulls: ArrowBuffer, stride: Int =
MemoryLayout<T>.stride) {
self.stride = stride
self.values = values
self.nulls = nulls
@@ -68,7 +68,7 @@ public class FixedBufferBuilder<T>: BaseBufferBuilder<T>,
ArrowBufferBuilder {
self.defaultVal = try FixedBufferBuilder<T>.defaultValueForType()
let values = ArrowBuffer.createBuffer(0, size:
UInt(MemoryLayout<T>.stride))
let nulls = ArrowBuffer.createBuffer(0, size:
UInt(MemoryLayout<UInt8>.stride))
- try super.init(values: values, nulls: nulls)
+ super.init(values: values, nulls: nulls)
}
public func append(_ newValue: ItemType?) {
@@ -134,7 +134,7 @@ public class FixedBufferBuilder<T>: BaseBufferBuilder<T>,
ArrowBufferBuilder {
return Double(0) as! T
}
- throw ValidationError.unknownType
+ throw ArrowError.unknownType
}
}
@@ -143,7 +143,7 @@ public class BoolBufferBuilder: BaseBufferBuilder<Bool>,
ArrowBufferBuilder {
public required init() throws {
let values = ArrowBuffer.createBuffer(0, size:
UInt(MemoryLayout<UInt8>.stride))
let nulls = ArrowBuffer.createBuffer(0, size:
UInt(MemoryLayout<UInt8>.stride))
- try super.init(values: values, nulls: nulls)
+ super.init(values: values, nulls: nulls)
}
public func append(_ newValue: ItemType?) {
@@ -198,7 +198,7 @@ public class VariableBufferBuilder<T>:
BaseBufferBuilder<T>, ArrowBufferBuilder
let values = ArrowBuffer.createBuffer(0, size: UInt(binaryStride))
let nulls = ArrowBuffer.createBuffer(0, size: UInt(binaryStride))
self.offsets = ArrowBuffer.createBuffer(0, size:
UInt(MemoryLayout<Int32>.stride))
- try super.init(values: values, nulls: nulls, stride: binaryStride)
+ super.init(values: values, nulls: nulls, stride: binaryStride)
}
public func append(_ newValue: ItemType?) {
diff --git a/swift/Arrow/Sources/Arrow/ArrowData.swift
b/swift/Arrow/Sources/Arrow/ArrowData.swift
index f70e5b551e..ead76b1c87 100644
--- a/swift/Arrow/Sources/Arrow/ArrowData.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowData.swift
@@ -28,11 +28,11 @@ public class ArrowData {
switch(type) {
case let .PrimitiveInfo(typeId):
if typeId == ArrowTypeId.Unknown {
- throw ValidationError.unknownType
+ throw ArrowError.unknownType
}
case let .VariableInfo(typeId):
if typeId == ArrowTypeId.Unknown {
- throw ValidationError.unknownType
+ throw ArrowError.unknownType
}
}
diff --git a/swift/Arrow/Sources/Arrow/ArrowReader.swift
b/swift/Arrow/Sources/Arrow/ArrowReader.swift
index 68647ca93a..151e0d1fd2 100644
--- a/swift/Arrow/Sources/Arrow/ArrowReader.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowReader.swift
@@ -18,10 +18,6 @@
import FlatBuffers
import Foundation
-public enum ArrowError: Error {
- case runtimeError(String)
-}
-
let FILEMARKER = "ARROW1"
let CONTINUATIONMARKER = -1
@@ -35,77 +31,101 @@ public class ArrowReader {
let messageOffset: Int64
}
- private func loadSchema(_ schema: org_apache_arrow_flatbuf_Schema) throws
-> ArrowSchema {
+ private func loadSchema(_ schema: org_apache_arrow_flatbuf_Schema) ->
Result<ArrowSchema, ArrowError> {
let builder = ArrowSchema.Builder()
for index in 0 ..< schema.fieldsCount {
let field = schema.fields(at: index)!
let arrowField = ArrowField(field.name!, type:
findArrowType(field), isNullable: field.nullable)
- let _ = builder.addField(arrowField)
+ builder.addField(arrowField)
if field.typeType == .struct_ {
- throw ValidationError.unknownType
+ return .failure(.unknownType)
}
}
- return builder.finish()
+ return .success(builder.finish())
}
- private func loadPrimitiveData(_ loadInfo: DataLoadInfo) throws ->
ChunkedArrayHolder {
- let node = loadInfo.recordBatch.nodes(at: loadInfo.nodeIndex)!
- try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex)
- let nullBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex)!
- let arrowNullBuffer = makeBuffer(nullBuffer, fileData:
loadInfo.fileData,
- length: UInt(node.nullCount),
messageOffset: loadInfo.messageOffset)
- try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex + 1)
- let valueBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex + 1)!
- let arrowValueBuffer = makeBuffer(valueBuffer, fileData:
loadInfo.fileData,
- length: UInt(node.length),
messageOffset: loadInfo.messageOffset)
- return try makeArrayHolder(loadInfo.field, buffers: [arrowNullBuffer,
arrowValueBuffer])
+ private func loadPrimitiveData(_ loadInfo: DataLoadInfo) ->
Result<ArrowArrayHolder, ArrowError> {
+ do {
+ let node = loadInfo.recordBatch.nodes(at: loadInfo.nodeIndex)!
+ try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex)
+ let nullBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex)!
+ let arrowNullBuffer = makeBuffer(nullBuffer, fileData:
loadInfo.fileData,
+ length: UInt(node.nullCount),
messageOffset: loadInfo.messageOffset)
+ try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex + 1)
+ let valueBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex + 1)!
+ let arrowValueBuffer = makeBuffer(valueBuffer, fileData:
loadInfo.fileData,
+ length: UInt(node.length),
messageOffset: loadInfo.messageOffset)
+ return makeArrayHolder(loadInfo.field, buffers: [arrowNullBuffer,
arrowValueBuffer])
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("\(error)"))
+ }
}
- private func loadVariableData(_ loadInfo: DataLoadInfo) throws ->
ChunkedArrayHolder {
+ private func loadVariableData(_ loadInfo: DataLoadInfo) ->
Result<ArrowArrayHolder, ArrowError> {
let node = loadInfo.recordBatch.nodes(at: loadInfo.nodeIndex)!
- try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex)
- let nullBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex)!
- let arrowNullBuffer = makeBuffer(nullBuffer, fileData:
loadInfo.fileData,
- length: UInt(node.nullCount),
messageOffset: loadInfo.messageOffset)
- try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex + 1)
- let offsetBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex + 1)!
- let arrowOffsetBuffer = makeBuffer(offsetBuffer, fileData:
loadInfo.fileData,
- length: UInt(node.length),
messageOffset: loadInfo.messageOffset)
- try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex + 2)
- let valueBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex + 2)!
- let arrowValueBuffer = makeBuffer(valueBuffer, fileData:
loadInfo.fileData,
- length: UInt(node.length),
messageOffset: loadInfo.messageOffset)
- return try makeArrayHolder(loadInfo.field, buffers: [arrowNullBuffer,
arrowOffsetBuffer, arrowValueBuffer])
+ do {
+ try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex)
+ let nullBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex)!
+ let arrowNullBuffer = makeBuffer(nullBuffer, fileData:
loadInfo.fileData,
+ length: UInt(node.nullCount),
messageOffset: loadInfo.messageOffset)
+ try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex + 1)
+ let offsetBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex + 1)!
+ let arrowOffsetBuffer = makeBuffer(offsetBuffer, fileData:
loadInfo.fileData,
+ length: UInt(node.length),
messageOffset: loadInfo.messageOffset)
+ try validateBufferIndex(loadInfo.recordBatch, index:
loadInfo.bufferIndex + 2)
+ let valueBuffer = loadInfo.recordBatch.buffers(at:
loadInfo.bufferIndex + 2)!
+ let arrowValueBuffer = makeBuffer(valueBuffer, fileData:
loadInfo.fileData,
+ length: UInt(node.length),
messageOffset: loadInfo.messageOffset)
+ return makeArrayHolder(loadInfo.field, buffers: [arrowNullBuffer,
arrowOffsetBuffer, arrowValueBuffer])
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("\(error)"))
+ }
}
private func loadRecordBatch(_ message: org_apache_arrow_flatbuf_Message,
schema: org_apache_arrow_flatbuf_Schema,
- data: Data, messageEndOffset: Int64) throws
-> RecordBatch {
+ data: Data, messageEndOffset: Int64) ->
Result<RecordBatch, ArrowError> {
let recordBatch = message.header(type:
org_apache_arrow_flatbuf_RecordBatch.self)
let nodesCount = recordBatch?.nodesCount ?? 0
var bufferIndex: Int32 = 0
- var columns: [ChunkedArrayHolder] = []
- let arrowSchema = try loadSchema(schema)
+ var columns: [ArrowArrayHolder] = []
for nodeIndex in 0 ..< nodesCount {
let field = schema.fields(at: nodeIndex)!
let loadInfo = DataLoadInfo(recordBatch: recordBatch!, field:
field,
nodeIndex: nodeIndex, bufferIndex:
bufferIndex,
fileData: data, messageOffset:
messageEndOffset)
+ var result: Result<ArrowArrayHolder, ArrowError>
if isFixedPrimitive(field.typeType) {
- let holder = try loadPrimitiveData(loadInfo)
- columns.append(holder)
+ result = loadPrimitiveData(loadInfo)
bufferIndex += 2
} else {
- let holder = try loadVariableData(loadInfo)
- columns.append(holder)
+ result = loadVariableData(loadInfo)
bufferIndex += 3
}
+
+ switch result {
+ case .success(let holder):
+ columns.append(holder)
+ case .failure(let error):
+ return .failure(error)
+ }
+
+ }
+
+ let schemaResult = loadSchema(schema)
+ switch schemaResult {
+ case .success(let arrowSchema):
+ return .success(RecordBatch(arrowSchema, columns: columns))
+ case .failure(let error):
+ return .failure(error)
}
-
- return RecordBatch(arrowSchema, columns: columns)
}
- public func fromStream(_ fileData: Data) throws -> [RecordBatch] {
+ public func fromStream(_ fileData: Data) -> Result<[RecordBatch],
ArrowError> {
let footerLength = fileData.withUnsafeBytes { rawBuffer in
rawBuffer.loadUnaligned(fromByteOffset: fileData.count - 4, as:
Int32.self)
}
@@ -138,26 +158,36 @@ public class ArrowReader {
let message =
org_apache_arrow_flatbuf_Message.getRootAsMessage(bb: mbb)
switch message.headerType {
case .recordbatch:
- let recordBatch = try loadRecordBatch(message, schema:
footer.schema!,
- data: fileData,
messageEndOffset: messageEndOffset)
- recordBatchs.append(recordBatch)
+ do {
+ let recordBatch = try loadRecordBatch(message, schema:
footer.schema!,
+ data: fileData,
messageEndOffset: messageEndOffset).get()
+ recordBatchs.append(recordBatch)
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("Unexpected error:
\(error)"))
+ }
default:
- print("Unhandled header type: \(message.headerType)")
+ return .failure(.unknownError("Unhandled header type:
\(message.headerType)"))
}
}
- return recordBatchs
+ return .success(recordBatchs)
}
- public func fromFile(_ fileURL: URL) throws -> [RecordBatch] {
- let fileData = try Data(contentsOf: fileURL)
- if !validateFileData(fileData) {
- throw ArrowError.runtimeError("Not a valid arrow file.")
- }
+ public func fromFile(_ fileURL: URL) -> Result<[RecordBatch], ArrowError> {
+ do {
+ let fileData = try Data(contentsOf: fileURL)
+ if !validateFileData(fileData) {
+ return .failure(.ioError("Not a valid arrow file."))
+ }
- let markerLength = FILEMARKER.utf8.count
- let footerLengthEnd = Int(fileData.count - markerLength)
- let data = fileData[..<(footerLengthEnd)]
- return try fromStream(data)
+ let markerLength = FILEMARKER.utf8.count
+ let footerLengthEnd = Int(fileData.count - markerLength)
+ let data = fileData[..<(footerLengthEnd)]
+ return fromStream(data)
+ } catch {
+ return .failure(.unknownError("Error loading file: \(error)"))
+ }
}
}
diff --git a/swift/Arrow/Sources/Arrow/ArrowReaderHelper.swift
b/swift/Arrow/Sources/Arrow/ArrowReaderHelper.swift
index c4e7b55b01..604b93f766 100644
--- a/swift/Arrow/Sources/Arrow/ArrowReaderHelper.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowReaderHelper.swift
@@ -18,15 +18,72 @@
import FlatBuffers
import Foundation
-fileprivate func makeFixedHolder<T>(_: T.Type, buffers: [ArrowBuffer]) throws
-> ChunkedArrayHolder {
- let nullCount = buffers[0].length
- let arrowData = try ArrowData(ArrowType.ArrowInt32, buffers: buffers,
- nullCount: nullCount, stride:
MemoryLayout<T>.stride)
- let chuckedArray = try ChunkedArray<T>([FixedArray<T>(arrowData)])
- return ChunkedArrayHolder(chuckedArray)
+fileprivate func makeStringHolder(_ buffers: [ArrowBuffer]) ->
Result<ArrowArrayHolder, ArrowError> {
+ do {
+ let arrowData = try ArrowData(ArrowType.ArrowString, buffers: buffers,
+ nullCount: buffers[0].length, stride:
MemoryLayout<Int8>.stride)
+ return .success(ArrowArrayHolder(StringArray(arrowData)))
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("\(error)"))
+ }
+}
+
+fileprivate func makeFloatHolder(_ floatType:
org_apache_arrow_flatbuf_FloatingPoint, buffers: [ArrowBuffer]) ->
Result<ArrowArrayHolder, ArrowError> {
+ switch floatType.precision {
+ case .single:
+ return makeFixedHolder(Float.self, buffers: buffers)
+ case .double:
+ return makeFixedHolder(Double.self, buffers: buffers)
+ default:
+ return .failure(.unknownType)
+ }
+}
+
+fileprivate func makeDateHolder(_ dateType: org_apache_arrow_flatbuf_Date,
buffers: [ArrowBuffer]) -> Result<ArrowArrayHolder, ArrowError> {
+ do {
+ if dateType.unit == .day {
+ let arrowData = try ArrowData(ArrowType.ArrowString, buffers:
buffers,
+ nullCount: buffers[0].length,
stride: MemoryLayout<Date>.stride)
+ return .success(ArrowArrayHolder(Date32Array(arrowData)))
+ }
+
+ let arrowData = try ArrowData(ArrowType.ArrowString, buffers: buffers,
+ nullCount: buffers[0].length, stride:
MemoryLayout<Date>.stride)
+ return .success(ArrowArrayHolder(Date64Array(arrowData)))
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("\(error)"))
+ }
+}
+
+fileprivate func makeBoolHolder(_ buffers: [ArrowBuffer]) ->
Result<ArrowArrayHolder, ArrowError> {
+ do {
+ let arrowData = try ArrowData(ArrowType.ArrowInt32, buffers: buffers,
+ nullCount: buffers[0].length, stride:
MemoryLayout<UInt8>.stride)
+ return .success(ArrowArrayHolder(BoolArray(arrowData)))
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("\(error)"))
+ }
+}
+
+fileprivate func makeFixedHolder<T>(_: T.Type, buffers: [ArrowBuffer]) ->
Result<ArrowArrayHolder, ArrowError> {
+ do {
+ let arrowData = try ArrowData(ArrowType.ArrowInt32, buffers: buffers,
+ nullCount: buffers[0].length, stride:
MemoryLayout<T>.stride)
+ return .success(ArrowArrayHolder(FixedArray<T>(arrowData)))
+ } catch let error as ArrowError {
+ return .failure(error)
+ } catch {
+ return .failure(.unknownError("\(error)"))
+ }
}
-func makeArrayHolder(_ field: org_apache_arrow_flatbuf_Field, buffers:
[ArrowBuffer]) throws -> ChunkedArrayHolder {
+func makeArrayHolder(_ field: org_apache_arrow_flatbuf_Field, buffers:
[ArrowBuffer]) -> Result<ArrowArrayHolder, ArrowError> {
let type = field.typeType
switch type {
case .int:
@@ -34,62 +91,42 @@ func makeArrayHolder(_ field:
org_apache_arrow_flatbuf_Field, buffers: [ArrowBuf
let bitWidth = intType.bitWidth
if bitWidth == 8 {
if intType.isSigned {
- return try makeFixedHolder(Int8.self, buffers: buffers)
+ return makeFixedHolder(Int8.self, buffers: buffers)
} else {
- return try makeFixedHolder(UInt8.self, buffers: buffers)
+ return makeFixedHolder(UInt8.self, buffers: buffers)
}
} else if bitWidth == 16 {
if intType.isSigned {
- return try makeFixedHolder(Int16.self, buffers: buffers)
+ return makeFixedHolder(Int16.self, buffers: buffers)
} else {
- return try makeFixedHolder(UInt16.self, buffers: buffers)
+ return makeFixedHolder(UInt16.self, buffers: buffers)
}
} else if bitWidth == 32 {
if intType.isSigned {
- return try makeFixedHolder(Int32.self, buffers: buffers)
+ return makeFixedHolder(Int32.self, buffers: buffers)
} else {
- return try makeFixedHolder(UInt32.self, buffers: buffers)
+ return makeFixedHolder(UInt32.self, buffers: buffers)
}
} else if bitWidth == 64 {
if intType.isSigned {
- return try makeFixedHolder(Int64.self, buffers: buffers)
+ return makeFixedHolder(Int64.self, buffers: buffers)
} else {
- return try makeFixedHolder(UInt64.self, buffers: buffers)
+ return makeFixedHolder(UInt64.self, buffers: buffers)
}
}
- throw ValidationError.unknownType
+ return .failure(.unknownType)
case .bool:
- let arrowData = try ArrowData(ArrowType.ArrowInt32, buffers: buffers,
- nullCount: buffers[0].length, stride:
MemoryLayout<UInt8>.stride)
- let chuckedArray = try ChunkedArray<Bool>([BoolArray(arrowData)])
- return ChunkedArrayHolder(chuckedArray)
+ return makeBoolHolder(buffers)
case .floatingpoint:
let floatType = field.type(type:
org_apache_arrow_flatbuf_FloatingPoint.self)!
- switch floatType.precision {
- case .single:
- return try makeFixedHolder(Float.self, buffers: buffers)
- case .double:
- return try makeFixedHolder(Double.self, buffers: buffers)
- default:
- throw ValidationError.unknownType
- }
+ return makeFloatHolder(floatType, buffers: buffers)
case .utf8:
- let arrowData = try ArrowData(ArrowType.ArrowString, buffers: buffers,
- nullCount: buffers[0].length, stride:
MemoryLayout<Int8>.stride)
- return ChunkedArrayHolder(try
ChunkedArray<String>([StringArray(arrowData)]))
+ return makeStringHolder(buffers)
case .date:
let dateType = field.type(type: org_apache_arrow_flatbuf_Date.self)!
- if dateType.unit == .day {
- let arrowData = try ArrowData(ArrowType.ArrowString, buffers:
buffers,
- nullCount: buffers[0].length,
stride: MemoryLayout<Date>.stride)
- return ChunkedArrayHolder(try
ChunkedArray<Date>([Date32Array(arrowData)]))
- }
-
- let arrowData = try ArrowData(ArrowType.ArrowString, buffers: buffers,
- nullCount: buffers[0].length, stride:
MemoryLayout<Date>.stride)
- return ChunkedArrayHolder(try
ChunkedArray<Date>([Date64Array(arrowData)]))
+ return makeDateHolder(dateType, buffers: buffers)
default:
- throw ValidationError.unknownType
+ return .failure(.unknownType)
}
}
@@ -149,7 +186,7 @@ func findArrowType(_ field: org_apache_arrow_flatbuf_Field)
-> ArrowType.Info {
func validateBufferIndex(_ recordBatch: org_apache_arrow_flatbuf_RecordBatch,
index: Int32) throws {
if index >= recordBatch.buffersCount {
- throw ArrowError.runtimeError("Buffer index is out of bounds:
\(index)")
+ throw ArrowError.outOfBounds(index: Int64(index))
}
}
diff --git a/swift/Arrow/Sources/Arrow/ArrowSchema.swift
b/swift/Arrow/Sources/Arrow/ArrowSchema.swift
index 4214403824..fc47ee7307 100644
--- a/swift/Arrow/Sources/Arrow/ArrowSchema.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowSchema.swift
@@ -52,11 +52,13 @@ public class ArrowSchema {
public class Builder {
private var fields: [ArrowField] = []
+ @discardableResult
public func addField(_ field: ArrowField) -> Builder {
fields.append(field)
return self
}
+ @discardableResult
public func addField(_ name: String, type: ArrowType.Info, isNullable:
Bool) -> Builder {
fields.append(ArrowField(name, type: type, isNullable: isNullable))
return self
diff --git a/swift/Arrow/Sources/Arrow/ArrowTable.swift
b/swift/Arrow/Sources/Arrow/ArrowTable.swift
index b8b87079f6..70cabfaa7b 100644
--- a/swift/Arrow/Sources/Arrow/ArrowTable.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowTable.swift
@@ -17,60 +17,6 @@
import Foundation
-public class ChunkedArrayHolder {
- public let type: ArrowType.Info
- public let length: UInt
- public let nullCount: UInt
- public let holder: Any
- public let getBufferData: () throws -> [Data]
- public let getBufferDataSizes: () throws -> [Int]
- public init<T>(_ chunked: ChunkedArray<T>) {
- self.holder = chunked
- self.length = chunked.length
- self.type = chunked.type
- self.nullCount = chunked.nullCount
- self.getBufferData = {() throws -> [Data] in
- var bufferData = [Data]()
- var numBuffers = 2;
- if !isFixedPrimitive(try toFBTypeEnum(chunked.type)) {
- numBuffers = 3
- }
-
- for _ in 0 ..< numBuffers {
- bufferData.append(Data())
- }
-
- for arrow_data in chunked.arrays {
- for index in 0 ..< numBuffers {
- arrow_data.arrowData.buffers[index].append(to:
&bufferData[index])
- }
- }
-
- return bufferData;
- }
-
- self.getBufferDataSizes = {() throws -> [Int] in
- var bufferDataSizes = [Int]()
- var numBuffers = 2;
- if !isFixedPrimitive(try toFBTypeEnum(chunked.type)) {
- numBuffers = 3
- }
- for _ in 0 ..< numBuffers {
- bufferDataSizes.append(Int(0))
- }
-
- for arrow_data in chunked.arrays {
- for index in 0 ..< numBuffers {
- bufferDataSizes[index] +=
Int(arrow_data.arrowData.buffers[index].capacity);
- }
- }
-
- return bufferDataSizes;
- }
-
- }
-}
-
public class ArrowColumn {
public let field: ArrowField
fileprivate let dataHolder: ChunkedArrayHolder
@@ -83,9 +29,9 @@ public class ArrowColumn {
}
public var name: String {get{return field.name}}
- public init<T>(_ field: ArrowField, chunked: ChunkedArray<T>) {
+ public init(_ field: ArrowField, chunked: ChunkedArrayHolder) {
self.field = field
- self.dataHolder = ChunkedArrayHolder(chunked)
+ self.dataHolder = chunked
}
}
@@ -100,43 +46,70 @@ public class ArrowTable {
self.rowCount = columns[0].length
}
- public func toRecordBatch() -> RecordBatch {
- var rbColumns = [ChunkedArrayHolder]()
- for column in self.columns {
- rbColumns.append(column.dataHolder)
+ public static func from(recordBatches: [RecordBatch]) ->
Result<ArrowTable, ArrowError> {
+ if(recordBatches.isEmpty) {
+ return .failure(.arrayHasNoElements)
+ }
+
+ var holders = [[ArrowArrayHolder]]()
+ let schema = recordBatches[0].schema;
+ for recordBatch in recordBatches {
+ for index in 0..<schema.fields.count {
+ if holders.count <= index {
+ holders.append([ArrowArrayHolder]())
+ }
+ holders[index].append(recordBatch.columns[index])
+ }
+ }
+
+ let builder = ArrowTable.Builder()
+ for index in 0..<schema.fields.count {
+ switch ArrowArrayHolder.makeArrowColumn(schema.fields[index],
holders: holders[index]) {
+ case .success(let column):
+ builder.addColumn(column)
+ case .failure(let error):
+ return .failure(error)
+ }
}
- return RecordBatch(schema, columns: rbColumns)
+ return .success(builder.finish())
}
public class Builder {
let schemaBuilder = ArrowSchema.Builder()
var columns = [ArrowColumn]()
+ @discardableResult
public func addColumn<T>(_ fieldName: String, arrowArray:
ArrowArray<T>) throws -> Builder {
return self.addColumn(fieldName, chunked: try
ChunkedArray([arrowArray]))
}
+ @discardableResult
public func addColumn<T>(_ fieldName: String, chunked:
ChunkedArray<T>) -> Builder {
let field = ArrowField(fieldName, type: chunked.type, isNullable:
chunked.nullCount != 0)
- let _ = self.schemaBuilder.addField(field)
- self.columns.append(ArrowColumn(field, chunked: chunked))
+ self.schemaBuilder.addField(field)
+ self.columns.append(ArrowColumn(field, chunked:
ChunkedArrayHolder(chunked)))
return self
}
+ @discardableResult
public func addColumn<T>(_ field: ArrowField, arrowArray:
ArrowArray<T>) throws -> Builder {
- let _ = self.schemaBuilder.addField(field)
- self.columns.append(ArrowColumn(field, chunked: try
ChunkedArray([arrowArray])))
+ self.schemaBuilder.addField(field)
+ let holder = ChunkedArrayHolder(try ChunkedArray([arrowArray]))
+ self.columns.append(ArrowColumn(field, chunked: holder))
return self
}
+ @discardableResult
public func addColumn<T>(_ field: ArrowField, chunked:
ChunkedArray<T>) -> Builder {
- let _ = self.schemaBuilder.addField(field)
- self.columns.append(ArrowColumn(field, chunked: chunked))
+ self.schemaBuilder.addField(field)
+ self.columns.append(ArrowColumn(field, chunked:
ChunkedArrayHolder(chunked)))
return self
}
+ @discardableResult
public func addColumn(_ column: ArrowColumn) -> Builder {
+ self.schemaBuilder.addField(column.field)
self.columns.append(column)
return self
}
@@ -150,9 +123,9 @@ public class ArrowTable {
public class RecordBatch {
let schema: ArrowSchema
var columnCount: UInt {get{return UInt(self.columns.count)}}
- let columns: [ChunkedArrayHolder]
+ let columns: [ArrowArrayHolder]
let length: UInt
- public init(_ schema: ArrowSchema, columns: [ChunkedArrayHolder]) {
+ public init(_ schema: ArrowSchema, columns: [ArrowArrayHolder]) {
self.schema = schema
self.columns = columns
self.length = columns[0].length
@@ -160,36 +133,46 @@ public class RecordBatch {
public class Builder {
let schemaBuilder = ArrowSchema.Builder()
- var columns = [ChunkedArrayHolder]()
+ var columns = [ArrowArrayHolder]()
- public func addColumn(_ fieldName: String, chunked:
ChunkedArrayHolder) -> Builder {
- let field = ArrowField(fieldName, type: chunked.type, isNullable:
chunked.nullCount != 0)
- let _ = self.schemaBuilder.addField(field)
- self.columns.append(chunked)
+ @discardableResult
+ public func addColumn(_ fieldName: String, arrowArray:
ArrowArrayHolder) -> Builder {
+ let field = ArrowField(fieldName, type: arrowArray.type,
isNullable: arrowArray.nullCount != 0)
+ self.schemaBuilder.addField(field)
+ self.columns.append(arrowArray)
return self
}
- public func addColumn(_ field: ArrowField, chunked:
ChunkedArrayHolder) -> Builder {
- let _ = self.schemaBuilder.addField(field)
- self.columns.append(chunked)
+ @discardableResult
+ public func addColumn(_ field: ArrowField, arrowArray:
ArrowArrayHolder) -> Builder {
+ self.schemaBuilder.addField(field)
+ self.columns.append(arrowArray)
return self
}
- public func finish() -> RecordBatch {
- return RecordBatch(self.schemaBuilder.finish(), columns:
self.columns)
+ public func finish() -> Result<RecordBatch, ArrowError> {
+ if columns.count > 0 {
+ let columnLength = columns[0].length
+ for column in columns {
+ if column.length != columnLength {
+ return .failure(.runtimeError("Columns have different
sizes"))
+ }
+ }
+ }
+ return .success(RecordBatch(self.schemaBuilder.finish(), columns:
self.columns))
}
}
- public func data<T>(for columnIndex: Int) -> ChunkedArray<T> {
+ public func data<T>(for columnIndex: Int) -> ArrowArray<T> {
let arrayHolder = column(columnIndex)
- return (arrayHolder.holder as! ChunkedArray<T>)
+ return (arrayHolder.array as! ArrowArray<T>)
}
- public func column(_ index: Int) -> ChunkedArrayHolder {
+ public func column(_ index: Int) -> ArrowArrayHolder {
return self.columns[index]
}
- public func column(_ name: String) -> ChunkedArrayHolder? {
+ public func column(_ name: String) -> ArrowArrayHolder? {
if let index = self.schema.fieldIndex(name) {
return self.columns[index]
}
diff --git a/swift/Arrow/Sources/Arrow/ArrowType.swift
b/swift/Arrow/Sources/Arrow/ArrowType.swift
index 0dc998287e..4064191918 100644
--- a/swift/Arrow/Sources/Arrow/ArrowType.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowType.swift
@@ -19,11 +19,17 @@ import Foundation
func FlatBuffersVersion_23_1_4() {
}
-
-public enum ValidationError: Error {
+
+public enum ArrowError: Error {
+ case none
case unknownType
- case outOfBounds(index: UInt)
+ case runtimeError(String)
+ case outOfBounds(index: Int64)
case arrayHasNoElements
+ case unknownError(String)
+ case notImplemented
+ case ioError(String)
+ case invalid(String)
}
public enum ArrowTypeId {
diff --git a/swift/Arrow/Sources/Arrow/ArrowWriter.swift
b/swift/Arrow/Sources/Arrow/ArrowWriter.swift
index 70b4d0ed18..c76dd94c13 100644
--- a/swift/Arrow/Sources/Arrow/ArrowWriter.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowWriter.swift
@@ -53,45 +53,70 @@ public class ArrowWriter {
}
}
- private func writeField(_ fbb: inout FlatBufferBuilder, field: ArrowField)
throws -> Offset {
+ private func writeField(_ fbb: inout FlatBufferBuilder, field: ArrowField)
-> Result<Offset, ArrowError> {
let nameOffset = fbb.create(string: field.name)
- let fieldTypeOfffset = try toFBType(&fbb, infoType: field.type)
+ let fieldTypeOffsetResult = toFBType(&fbb, infoType: field.type)
let startOffset = org_apache_arrow_flatbuf_Field.startField(&fbb)
org_apache_arrow_flatbuf_Field.add(name: nameOffset, &fbb)
org_apache_arrow_flatbuf_Field.add(nullable: field.isNullable, &fbb)
- org_apache_arrow_flatbuf_Field.add(typeType: try
toFBTypeEnum(field.type), &fbb)
- org_apache_arrow_flatbuf_Field.add(type: fieldTypeOfffset, &fbb)
- return org_apache_arrow_flatbuf_Field.endField(&fbb, start:
startOffset)
+ switch toFBTypeEnum(field.type) {
+ case .success(let type):
+ org_apache_arrow_flatbuf_Field.add(typeType: type, &fbb)
+ case .failure(let error):
+ return .failure(error)
+ }
+
+ switch fieldTypeOffsetResult {
+ case .success(let offset):
+ org_apache_arrow_flatbuf_Field.add(type: offset, &fbb)
+ return .success(org_apache_arrow_flatbuf_Field.endField(&fbb,
start: startOffset))
+ case .failure(let error):
+ return .failure(error)
+ }
}
- private func writeSchema(_ fbb: inout FlatBufferBuilder, schema:
ArrowSchema) throws -> Offset {
+ private func writeSchema(_ fbb: inout FlatBufferBuilder, schema:
ArrowSchema) -> Result<Offset, ArrowError> {
var fieldOffsets = [Offset]()
for field in schema.fields {
- fieldOffsets.append(try writeField(&fbb, field: field))
+ switch writeField(&fbb, field: field) {
+ case .success(let offset):
+ fieldOffsets.append(offset)
+ case .failure(let error):
+ return .failure(error)
+ }
+
}
let fieldsOffset: Offset = fbb.createVector(ofOffsets: fieldOffsets)
let schemaOffset = org_apache_arrow_flatbuf_Schema.createSchema(&fbb,
endianness: .little, fieldsVectorOffset: fieldsOffset)
- return schemaOffset
+ return .success(schemaOffset)
}
- private func writeRecordBatches(_ writer: inout DataWriter, batches:
[RecordBatch]) throws -> [org_apache_arrow_flatbuf_Block] {
+ private func writeRecordBatches(_ writer: inout DataWriter, batches:
[RecordBatch]) -> Result<[org_apache_arrow_flatbuf_Block], ArrowError> {
var rbBlocks = [org_apache_arrow_flatbuf_Block]()
for batch in batches {
let startIndex = writer.count
- let rbResult = try writeRecordBatch(batch: batch)
- withUnsafeBytes(of: rbResult.1.o.littleEndian)
{writer.append(Data($0))}
- writer.append(rbResult.0)
- try writeRecordBatchData(&writer, batch: batch)
- rbBlocks.append(org_apache_arrow_flatbuf_Block(offset:
Int64(startIndex), metaDataLength: Int32(0), bodyLength: Int64(rbResult.1.o)))
+ switch writeRecordBatch(batch: batch) {
+ case .success(let rbResult):
+ withUnsafeBytes(of: rbResult.1.o.littleEndian)
{writer.append(Data($0))}
+ writer.append(rbResult.0)
+ switch writeRecordBatchData(&writer, batch: batch) {
+ case .success(_):
+ rbBlocks.append(org_apache_arrow_flatbuf_Block(offset:
Int64(startIndex), metaDataLength: Int32(0), bodyLength: Int64(rbResult.1.o)))
+ case .failure(let error):
+ return .failure(error)
+ }
+ case .failure(let error):
+ return .failure(error)
+ }
}
- return rbBlocks
+ return .success(rbBlocks)
}
- private func writeRecordBatch(batch: RecordBatch) throws -> (Data, Offset)
{
+ private func writeRecordBatch(batch: RecordBatch) -> Result<(Data,
Offset), ArrowError> {
let schema = batch.schema
var output = Data()
var fbb = FlatBufferBuilder()
@@ -112,7 +137,7 @@ public class ArrowWriter {
var bufferOffset = Int(0)
for index in 0 ..< batch.schema.fields.count {
let column = batch.column(index)
- let colBufferDataSizes = try column.getBufferDataSizes()
+ let colBufferDataSizes = column.getBufferDataSizes()
for var bufferDataSize in colBufferDataSizes {
bufferDataSize = getPadForAlignment(bufferDataSize)
let buffer = org_apache_arrow_flatbuf_Buffer(offset:
Int64(bufferOffset), length: Int64(bufferDataSize))
@@ -140,66 +165,95 @@ public class ArrowWriter {
let messageOffset = org_apache_arrow_flatbuf_Message.endMessage(&fbb,
start: startMessage)
fbb.finish(offset: messageOffset)
output.append(fbb.data)
- return (output, Offset(offset: UInt32(output.count)))
+ return .success((output, Offset(offset: UInt32(output.count))))
}
- private func writeRecordBatchData(_ writer: inout DataWriter, batch:
RecordBatch) throws {
+ private func writeRecordBatchData(_ writer: inout DataWriter, batch:
RecordBatch) -> Result<Bool, ArrowError> {
for index in 0 ..< batch.schema.fields.count {
let column = batch.column(index)
- let colBufferData = try column.getBufferData()
+ let colBufferData = column.getBufferData();
for var bufferData in colBufferData {
addPadForAlignment(&bufferData)
writer.append(bufferData)
}
}
+
+ return .success(true)
}
- private func writeFooter(schema: ArrowSchema, rbBlocks:
[org_apache_arrow_flatbuf_Block]) throws -> Data {
+ private func writeFooter(schema: ArrowSchema, rbBlocks:
[org_apache_arrow_flatbuf_Block]) -> Result<Data, ArrowError> {
var fbb: FlatBufferBuilder = FlatBufferBuilder()
- let schemaOffset = try writeSchema(&fbb, schema: schema)
-
- let _ = fbb.startVector(rbBlocks.count, elementSize:
MemoryLayout<org_apache_arrow_flatbuf_Block>.size)
- for blkInfo in rbBlocks.reversed() {
- fbb.create(struct: blkInfo)
+ switch writeSchema(&fbb, schema: schema) {
+ case .success(let schemaOffset):
+ fbb.startVector(rbBlocks.count, elementSize:
MemoryLayout<org_apache_arrow_flatbuf_Block>.size)
+ for blkInfo in rbBlocks.reversed() {
+ fbb.create(struct: blkInfo)
+ }
+
+ let rbBlkEnd = fbb.endVector(len: rbBlocks.count)
+
+
+ let footerStartOffset =
org_apache_arrow_flatbuf_Footer.startFooter(&fbb)
+ org_apache_arrow_flatbuf_Footer.add(schema: schemaOffset, &fbb)
+ org_apache_arrow_flatbuf_Footer.addVectorOf(recordBatches:
rbBlkEnd, &fbb)
+ let footerOffset = org_apache_arrow_flatbuf_Footer.endFooter(&fbb,
start: footerStartOffset)
+ fbb.finish(offset: footerOffset)
+ case .failure(let error):
+ return .failure(error)
}
-
- let rbBlkEnd = fbb.endVector(len: rbBlocks.count)
-
- let footerStartOffset =
org_apache_arrow_flatbuf_Footer.startFooter(&fbb)
- org_apache_arrow_flatbuf_Footer.add(schema: schemaOffset, &fbb)
- org_apache_arrow_flatbuf_Footer.addVectorOf(recordBatches: rbBlkEnd,
&fbb)
- let footerOffset = org_apache_arrow_flatbuf_Footer.endFooter(&fbb,
start: footerStartOffset)
- fbb.finish(offset: footerOffset)
- return fbb.data
+ return .success(fbb.data)
}
- private func writeStream(_ writer: inout DataWriter, schema: ArrowSchema,
batches: [RecordBatch]) throws {
+ private func writeStream(_ writer: inout DataWriter, schema: ArrowSchema,
batches: [RecordBatch]) -> Result<Bool, ArrowError> {
var fbb: FlatBufferBuilder = FlatBufferBuilder()
- let schemaOffset = try writeSchema(&fbb, schema: schema)
- fbb.finish(offset: schemaOffset)
- writer.append(fbb.data)
-
- let rbBlocks = try writeRecordBatches(&writer, batches: batches)
- let footerData = try writeFooter(schema: schema, rbBlocks: rbBlocks)
- fbb.finish(offset: Offset(offset: fbb.buffer.size))
- let footerOffset = writer.count
- writer.append(footerData)
- addPadForAlignment(&writer)
+ switch writeSchema(&fbb, schema: schema) {
+ case .success(let schemaOffset):
+ fbb.finish(offset: schemaOffset)
+ writer.append(fbb.data)
+ case .failure(let error):
+ return .failure(error)
+ }
- withUnsafeBytes(of: Int32(0).littleEndian) { writer.append(Data($0)) }
- let footerDiff = (UInt32(writer.count) - UInt32(footerOffset));
- withUnsafeBytes(of: footerDiff.littleEndian) { writer.append(Data($0))
}
+ switch writeRecordBatches(&writer, batches: batches) {
+ case .success(let rbBlocks):
+ switch writeFooter(schema: schema, rbBlocks: rbBlocks) {
+ case .success(let footerData):
+ fbb.finish(offset: Offset(offset: fbb.buffer.size))
+ let footerOffset = writer.count
+ writer.append(footerData)
+ addPadForAlignment(&writer)
+
+ withUnsafeBytes(of: Int32(0).littleEndian) {
writer.append(Data($0)) }
+ let footerDiff = (UInt32(writer.count) - UInt32(footerOffset));
+ withUnsafeBytes(of: footerDiff.littleEndian) {
writer.append(Data($0)) }
+ case .failure(let error):
+ return .failure(error)
+ }
+ case .failure(let error):
+ return .failure(error)
+ }
+
+ return .success(true)
}
- public func toStream(_ schema: ArrowSchema, batches: [RecordBatch]) throws
-> Data {
+ public func toStream(_ schema: ArrowSchema, batches: [RecordBatch]) ->
Result<Data, ArrowError> {
var writer: any DataWriter = InMemDataWriter()
- try writeStream(&writer, schema: schema, batches: batches)
- return (writer as! InMemDataWriter).data
+ switch writeStream(&writer, schema: schema, batches: batches) {
+ case .success(_):
+ return .success((writer as! InMemDataWriter).data)
+ case .failure(let error):
+ return .failure(error)
+ }
}
- public func toFile(_ fileName: URL, schema: ArrowSchema, batches:
[RecordBatch]) throws {
- try Data().write(to: fileName)
+ public func toFile(_ fileName: URL, schema: ArrowSchema, batches:
[RecordBatch]) -> Result<Bool, ArrowError> {
+ do {
+ try Data().write(to: fileName)
+ } catch {
+ return .failure(.ioError("\(error)"))
+ }
+
let fileHandle = FileHandle(forUpdatingAtPath: fileName.path)!
defer { fileHandle.closeFile() }
@@ -208,7 +262,13 @@ public class ArrowWriter {
var writer: any DataWriter = FileDataWriter(fileHandle)
writer.append(FILEMARKER.data(using: .utf8)!)
- try writeStream(&writer, schema: schema, batches: batches)
- writer.append(FILEMARKER.data(using: .utf8)!)
+ switch writeStream(&writer, schema: schema, batches: batches) {
+ case .success(_):
+ writer.append(FILEMARKER.data(using: .utf8)!)
+ case .failure(let error):
+ return .failure(error)
+ }
+
+ return .success(true)
}
}
diff --git a/swift/Arrow/Sources/Arrow/ArrowWriterHelper.swift
b/swift/Arrow/Sources/Arrow/ArrowWriterHelper.swift
index 520d4f0614..75f76be1e2 100644
--- a/swift/Arrow/Sources/Arrow/ArrowWriterHelper.swift
+++ b/swift/Arrow/Sources/Arrow/ArrowWriterHelper.swift
@@ -24,53 +24,53 @@ extension Data {
}
}
-func toFBTypeEnum(_ infoType: ArrowType.Info) throws ->
org_apache_arrow_flatbuf_Type_ {
+func toFBTypeEnum(_ infoType: ArrowType.Info) ->
Result<org_apache_arrow_flatbuf_Type_, ArrowError> {
if infoType == ArrowType.ArrowInt8 || infoType == ArrowType.ArrowInt16 ||
infoType == ArrowType.ArrowInt64 || infoType == ArrowType.ArrowUInt8 ||
infoType == ArrowType.ArrowUInt16 || infoType == ArrowType.ArrowUInt32
||
infoType == ArrowType.ArrowUInt64 || infoType == ArrowType.ArrowInt32 {
- return org_apache_arrow_flatbuf_Type_.int
+ return .success(org_apache_arrow_flatbuf_Type_.int)
} else if infoType == ArrowType.ArrowFloat || infoType ==
ArrowType.ArrowDouble {
- return org_apache_arrow_flatbuf_Type_.floatingpoint
+ return .success(org_apache_arrow_flatbuf_Type_.floatingpoint)
} else if infoType == ArrowType.ArrowString {
- return org_apache_arrow_flatbuf_Type_.utf8
+ return .success(org_apache_arrow_flatbuf_Type_.utf8)
} else if infoType == ArrowType.ArrowBool {
- return org_apache_arrow_flatbuf_Type_.bool
+ return .success(org_apache_arrow_flatbuf_Type_.bool)
} else if infoType == ArrowType.ArrowDate32 || infoType ==
ArrowType.ArrowDate64 {
- return org_apache_arrow_flatbuf_Type_.date
+ return .success(org_apache_arrow_flatbuf_Type_.date)
}
- throw ValidationError.unknownType
+ return .failure(.unknownType)
}
-func toFBType(_ fbb: inout FlatBufferBuilder, infoType: ArrowType.Info) throws
-> Offset {
+func toFBType(_ fbb: inout FlatBufferBuilder, infoType: ArrowType.Info) ->
Result<Offset, ArrowError> {
if infoType == ArrowType.ArrowInt8 || infoType == ArrowType.ArrowUInt8 {
- return org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth: 8,
isSigned: infoType == ArrowType.ArrowInt8);
+ return .success(org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth:
8, isSigned: infoType == ArrowType.ArrowInt8))
} else if infoType == ArrowType.ArrowInt16 || infoType ==
ArrowType.ArrowUInt16 {
- return org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth: 16,
isSigned: infoType == ArrowType.ArrowInt16);
+ return .success(org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth:
16, isSigned: infoType == ArrowType.ArrowInt16))
} else if infoType == ArrowType.ArrowInt32 || infoType ==
ArrowType.ArrowUInt32 {
- return org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth: 32,
isSigned: infoType == ArrowType.ArrowInt32);
+ return .success(org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth:
32, isSigned: infoType == ArrowType.ArrowInt32))
} else if infoType == ArrowType.ArrowInt64 || infoType ==
ArrowType.ArrowUInt64 {
- return org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth: 64,
isSigned: infoType == ArrowType.ArrowInt64);
+ return .success(org_apache_arrow_flatbuf_Int.createInt(&fbb, bitWidth:
64, isSigned: infoType == ArrowType.ArrowInt64))
} else if infoType == ArrowType.ArrowFloat {
- return
org_apache_arrow_flatbuf_FloatingPoint.createFloatingPoint(&fbb, precision:
.single)
+ return
.success(org_apache_arrow_flatbuf_FloatingPoint.createFloatingPoint(&fbb,
precision: .single))
} else if infoType == ArrowType.ArrowDouble {
- return
org_apache_arrow_flatbuf_FloatingPoint.createFloatingPoint(&fbb, precision:
.double)
+ return
.success(org_apache_arrow_flatbuf_FloatingPoint.createFloatingPoint(&fbb,
precision: .double))
} else if infoType == ArrowType.ArrowString {
- return org_apache_arrow_flatbuf_Utf8.endUtf8(&fbb, start:
org_apache_arrow_flatbuf_Utf8.startUtf8(&fbb))
+ return .success(org_apache_arrow_flatbuf_Utf8.endUtf8(&fbb, start:
org_apache_arrow_flatbuf_Utf8.startUtf8(&fbb)))
} else if infoType == ArrowType.ArrowBool {
- return org_apache_arrow_flatbuf_Bool.endBool(&fbb, start:
org_apache_arrow_flatbuf_Bool.startBool(&fbb))
+ return .success(org_apache_arrow_flatbuf_Bool.endBool(&fbb, start:
org_apache_arrow_flatbuf_Bool.startBool(&fbb)))
} else if infoType == ArrowType.ArrowDate32 {
let startOffset = org_apache_arrow_flatbuf_Date.startDate(&fbb)
org_apache_arrow_flatbuf_Date.add(unit: .day, &fbb)
- return org_apache_arrow_flatbuf_Date.endDate(&fbb, start: startOffset)
+ return .success(org_apache_arrow_flatbuf_Date.endDate(&fbb, start:
startOffset))
} else if infoType == ArrowType.ArrowDate64 {
let startOffset = org_apache_arrow_flatbuf_Date.startDate(&fbb)
org_apache_arrow_flatbuf_Date.add(unit: .millisecond, &fbb)
- return org_apache_arrow_flatbuf_Date.endDate(&fbb, start: startOffset)
+ return .success(org_apache_arrow_flatbuf_Date.endDate(&fbb, start:
startOffset))
}
-
- throw ValidationError.unknownType
+
+ return .failure(.unknownType)
}
func addPadForAlignment(_ data: inout Data, alignment: Int = 8) {
diff --git a/swift/Arrow/Sources/Arrow/ChunkedArray.swift
b/swift/Arrow/Sources/Arrow/ChunkedArray.swift
index 2e060b3b66..c23c953290 100644
--- a/swift/Arrow/Sources/Arrow/ChunkedArray.swift
+++ b/swift/Arrow/Sources/Arrow/ChunkedArray.swift
@@ -20,6 +20,73 @@ import Foundation
public protocol AsString {
func asString(_ index: UInt) -> String
}
+
+public class ChunkedArrayHolder {
+ public let type: ArrowType.Info
+ public let length: UInt
+ public let nullCount: UInt
+ public let holder: Any
+ public let getBufferData: () -> Result<[Data], ArrowError>
+ public let getBufferDataSizes: () -> Result<[Int], ArrowError>
+ public init<T>(_ chunked: ChunkedArray<T>) {
+ self.holder = chunked
+ self.length = chunked.length
+ self.type = chunked.type
+ self.nullCount = chunked.nullCount
+ self.getBufferData = {() -> Result<[Data], ArrowError> in
+ var bufferData = [Data]()
+ var numBuffers = 2;
+ switch toFBTypeEnum(chunked.type) {
+ case .success(let fbType):
+ if !isFixedPrimitive(fbType) {
+ numBuffers = 3
+ }
+ case .failure(let error):
+ return .failure(error)
+ }
+
+ for _ in 0 ..< numBuffers {
+ bufferData.append(Data())
+ }
+
+ for arrow_data in chunked.arrays {
+ for index in 0 ..< numBuffers {
+ arrow_data.arrowData.buffers[index].append(to:
&bufferData[index])
+ }
+ }
+
+ return .success(bufferData);
+ }
+
+ self.getBufferDataSizes = {() -> Result<[Int], ArrowError> in
+ var bufferDataSizes = [Int]()
+ var numBuffers = 2;
+
+ switch toFBTypeEnum(chunked.type) {
+ case .success(let fbType):
+ if !isFixedPrimitive(fbType) {
+ numBuffers = 3
+ }
+ case .failure(let error):
+ return .failure(error)
+ }
+
+ for _ in 0 ..< numBuffers {
+ bufferDataSizes.append(Int(0))
+ }
+
+ for arrow_data in chunked.arrays {
+ for index in 0 ..< numBuffers {
+ bufferDataSizes[index] +=
Int(arrow_data.arrowData.buffers[index].capacity)
+ }
+ }
+
+ return .success(bufferDataSizes)
+ }
+
+ }
+}
+
public class ChunkedArray<T> : AsString {
public let arrays: [ArrowArray<T>]
public let type: ArrowType.Info
@@ -29,7 +96,7 @@ public class ChunkedArray<T> : AsString {
public init(_ arrays: [ArrowArray<T>]) throws {
if arrays.count == 0 {
- throw ValidationError.arrayHasNoElements
+ throw ArrowError.arrayHasNoElements
}
self.type = arrays[0].arrowData.type
@@ -53,7 +120,7 @@ public class ChunkedArray<T> : AsString {
var localIndex = index
var arrayIndex = 0;
var len: UInt = arrays[arrayIndex].length
- while localIndex > len {
+ while localIndex > (len - 1) {
arrayIndex += 1
if arrayIndex > arrays.count {
return nil
diff --git a/swift/Arrow/Tests/ArrowTests/IPCTests.swift
b/swift/Arrow/Tests/ArrowTests/IPCTests.swift
index f57f0435ad..1aa6873dcb 100644
--- a/swift/Arrow/Tests/ArrowTests/IPCTests.swift
+++ b/swift/Arrow/Tests/ArrowTests/IPCTests.swift
@@ -19,7 +19,16 @@ import XCTest
import FlatBuffers
@testable import Arrow
-func checkBoolRecordBatch(_ recordBatches: [RecordBatch]) {
+@discardableResult
+func checkBoolRecordBatch(_ result: Result<[RecordBatch], ArrowError>) throws
-> [RecordBatch] {
+ let recordBatches: [RecordBatch]
+ switch result {
+ case .success(let rbBatches):
+ recordBatches = rbBatches
+ case .failure(let error):
+ throw error
+ }
+
XCTAssertEqual(recordBatches.count, 1)
for recordBatch in recordBatches {
XCTAssertEqual(recordBatch.length, 5)
@@ -31,7 +40,7 @@ func checkBoolRecordBatch(_ recordBatches: [RecordBatch]) {
XCTAssertEqual(recordBatch.schema.fields[1].type,
ArrowType.ArrowString)
for index in 0..<recordBatch.length {
let column = recordBatch.columns[0]
- let str = column.holder as! AsString
+ let str = column.array as! AsString
let val = "\(str.asString(index))"
if index == 0 || index == 4 {
XCTAssertEqual(val, "true")
@@ -42,6 +51,8 @@ func checkBoolRecordBatch(_ recordBatches: [RecordBatch]) {
}
}
}
+
+ return recordBatches
}
func currentDirectory(path: String = #file) -> URL {
@@ -52,7 +63,15 @@ final class IPCFileReaderTests: XCTestCase {
func testFileReader_double() throws {
let fileURL =
currentDirectory().appendingPathComponent("../../testdata_double.arrow")
let arrowReader = ArrowReader()
- let recordBatches = try arrowReader.fromFile(fileURL)
+ let result = arrowReader.fromFile(fileURL)
+ let recordBatches: [RecordBatch]
+ switch result {
+ case .success(let rbBatches):
+ recordBatches = rbBatches
+ case .failure(let error):
+ throw error
+ }
+
XCTAssertEqual(recordBatches.count, 1)
for recordBatch in recordBatches {
XCTAssertEqual(recordBatch.length, 5)
@@ -64,7 +83,7 @@ final class IPCFileReaderTests: XCTestCase {
XCTAssertEqual(recordBatch.schema.fields[1].type,
ArrowType.ArrowString)
for index in 0..<recordBatch.length {
let column = recordBatch.columns[1]
- let str = column.holder as! AsString
+ let str = column.array as! AsString
let val = "\(str.asString(index))"
if index != 1 {
XCTAssertNotEqual(val, "")
@@ -78,28 +97,37 @@ final class IPCFileReaderTests: XCTestCase {
func testFileReader_bool() throws {
let fileURL =
currentDirectory().appendingPathComponent("../../testdata_bool.arrow")
let arrowReader = ArrowReader()
- let fileRBs = try arrowReader.fromFile(fileURL)
- checkBoolRecordBatch(fileRBs)
+ try checkBoolRecordBatch(arrowReader.fromFile(fileURL))
}
+
func testFileWriter_bool() throws {
//read existing file
let fileURL =
currentDirectory().appendingPathComponent("../../testdata_bool.arrow")
let arrowReader = ArrowReader()
- let fileRBs = try arrowReader.fromFile(fileURL)
- checkBoolRecordBatch(fileRBs)
+ let fileRBs = try checkBoolRecordBatch(arrowReader.fromFile(fileURL))
let arrowWriter = ArrowWriter()
//write data from file to a stream
- let writeData = try arrowWriter.toStream(fileRBs[0].schema, batches:
fileRBs)
- //read stream back into recordbatches
- checkBoolRecordBatch(try arrowReader.fromStream(writeData))
+ switch arrowWriter.toStream(fileRBs[0].schema, batches: fileRBs) {
+ case .success(let writeData):
+ //read stream back into recordbatches
+ try checkBoolRecordBatch(arrowReader.fromStream(writeData))
+ case .failure(let error):
+ throw error
+ }
+
//write file record batches to another file
let outputUrl =
currentDirectory().appendingPathComponent("../../testfilewriter_bool.arrow")
- try arrowWriter.toFile(outputUrl, schema: fileRBs[0].schema, batches:
fileRBs)
- checkBoolRecordBatch(try arrowReader.fromFile(outputUrl))
+ switch arrowWriter.toFile(outputUrl, schema: fileRBs[0].schema,
batches: fileRBs) {
+ case .success(_):
+ try checkBoolRecordBatch(arrowReader.fromFile(outputUrl))
+ case .failure(let error):
+ throw error
+ }
+
}
- func makeSchema() throws -> ArrowSchema {
+ func makeSchema() -> ArrowSchema {
let schemaBuilder = ArrowSchema.Builder();
return schemaBuilder.addField("col1", type: ArrowType.ArrowUInt8,
isNullable: true)
.addField("col2", type: ArrowType.ArrowString, isNullable: false)
@@ -111,50 +139,74 @@ final class IPCFileReaderTests: XCTestCase {
let uint8Builder: NumberArrayBuilder<UInt8> = try
ArrowArrayBuilders.loadNumberArrayBuilder();
uint8Builder.append(10)
uint8Builder.append(22)
+ uint8Builder.append(33)
+ uint8Builder.append(44)
let stringBuilder = try ArrowArrayBuilders.loadStringArrayBuilder();
stringBuilder.append("test10")
stringBuilder.append("test22")
+ stringBuilder.append("test33")
+ stringBuilder.append("test44")
let date32Builder = try ArrowArrayBuilders.loadDate32ArrayBuilder();
let date2 = Date(timeIntervalSinceReferenceDate: 86400 * 1)
let date1 = Date(timeIntervalSinceReferenceDate: 86400 * 5000 + 352)
date32Builder.append(date1)
date32Builder.append(date2)
-
- let intHolder = ChunkedArrayHolder(try
ChunkedArray([uint8Builder.finish()]))
- let stringHolder = ChunkedArrayHolder(try
ChunkedArray([stringBuilder.finish()]))
- let date32Holder = ChunkedArrayHolder(try
ChunkedArray([date32Builder.finish()]))
- return RecordBatch.Builder()
- .addColumn("col1", chunked: intHolder)
- .addColumn("col2", chunked: stringHolder)
- .addColumn("col3", chunked: date32Holder)
+ date32Builder.append(date1)
+ date32Builder.append(date2)
+
+ let intHolder = ArrowArrayHolder(try uint8Builder.finish())
+ let stringHolder = ArrowArrayHolder(try stringBuilder.finish())
+ let date32Holder = ArrowArrayHolder(try date32Builder.finish())
+ let result = RecordBatch.Builder()
+ .addColumn("col1", arrowArray: intHolder)
+ .addColumn("col2", arrowArray: stringHolder)
+ .addColumn("col3", arrowArray: date32Holder)
.finish()
+ switch result {
+ case .success(let recordBatch):
+ return recordBatch
+ case .failure(let error):
+ throw error
+ }
}
func testInMemoryToFromStream() throws {
//read existing file
- let schema = try makeSchema()
+ let schema = makeSchema()
let recordBatch = try makeRecordBatch()
let arrowWriter = ArrowWriter()
- let writeData = try arrowWriter.toStream(schema, batches:
[recordBatch])
- let arrowReader = ArrowReader()
- let recordBatches = try arrowReader.fromStream(writeData)
- XCTAssertEqual(recordBatches.count, 1)
- for recordBatch in recordBatches {
- XCTAssertEqual(recordBatch.length, 2)
- XCTAssertEqual(recordBatch.columns.count, 3)
- XCTAssertEqual(recordBatch.schema.fields.count, 3)
- XCTAssertEqual(recordBatch.schema.fields[0].name, "col1")
- XCTAssertEqual(recordBatch.schema.fields[0].type,
ArrowType.ArrowUInt8)
- XCTAssertEqual(recordBatch.schema.fields[1].name, "col2")
- XCTAssertEqual(recordBatch.schema.fields[1].type,
ArrowType.ArrowString)
- XCTAssertEqual(recordBatch.schema.fields[2].name, "col3")
- XCTAssertEqual(recordBatch.schema.fields[2].type,
ArrowType.ArrowDate32)
- let dateVal = "\((recordBatch.columns[2].holder as!
AsString).asString(0))"
- XCTAssertEqual(dateVal, "2014-09-10 00:00:00 +0000")
- let stringVal = "\((recordBatch.columns[1].holder as!
AsString).asString(1))"
- XCTAssertEqual(stringVal, "test22")
- let uintVal = "\((recordBatch.columns[0].holder as!
AsString).asString(0))"
- XCTAssertEqual(uintVal, "10")
+ switch arrowWriter.toStream(schema, batches: [recordBatch]) {
+ case .success(let writeData):
+ let arrowReader = ArrowReader()
+ switch arrowReader.fromStream(writeData) {
+ case .success(let recordBatches):
+ XCTAssertEqual(recordBatches.count, 1)
+ for recordBatch in recordBatches {
+ XCTAssertEqual(recordBatch.length, 4)
+ XCTAssertEqual(recordBatch.columns.count, 3)
+ XCTAssertEqual(recordBatch.schema.fields.count, 3)
+ XCTAssertEqual(recordBatch.schema.fields[0].name, "col1")
+ XCTAssertEqual(recordBatch.schema.fields[0].type,
ArrowType.ArrowUInt8)
+ XCTAssertEqual(recordBatch.schema.fields[1].name, "col2")
+ XCTAssertEqual(recordBatch.schema.fields[1].type,
ArrowType.ArrowString)
+ XCTAssertEqual(recordBatch.schema.fields[2].name, "col3")
+ XCTAssertEqual(recordBatch.schema.fields[2].type,
ArrowType.ArrowDate32)
+ let dateVal = "\((recordBatch.columns[2].array as!
AsString).asString(0))"
+ XCTAssertEqual(dateVal, "2014-09-10 00:00:00 +0000")
+ let stringVal = "\((recordBatch.columns[1].array as!
AsString).asString(1))"
+ XCTAssertEqual(stringVal, "test22")
+ let uintVal = "\((recordBatch.columns[0].array as!
AsString).asString(0))"
+ XCTAssertEqual(uintVal, "10")
+ let stringVal2 = "\((recordBatch.columns[1].array as!
AsString).asString(3))"
+ XCTAssertEqual(stringVal2, "test44")
+ let uintVal2 = "\((recordBatch.columns[0].array as!
AsString).asString(3))"
+ XCTAssertEqual(uintVal2, "44")
+ }
+ case.failure(let error):
+ throw error
+ }
+ case .failure(let error):
+ throw error
}
}
}
diff --git a/swift/Arrow/Tests/ArrowTests/RecordBatchTests.swift
b/swift/Arrow/Tests/ArrowTests/RecordBatchTests.swift
index 8af34d5c9d..5da4461765 100644
--- a/swift/Arrow/Tests/ArrowTests/RecordBatchTests.swift
+++ b/swift/Arrow/Tests/ArrowTests/RecordBatchTests.swift
@@ -27,25 +27,29 @@ final class RecordBatchTests: XCTestCase {
stringBuilder.append("test10")
stringBuilder.append("test22")
- let intHolder = ChunkedArrayHolder(try
ChunkedArray([uint8Builder.finish()]))
- let stringHolder = ChunkedArrayHolder(try
ChunkedArray([stringBuilder.finish()]))
- let recordBatch = RecordBatch.Builder()
- .addColumn("col1", chunked: intHolder)
- .addColumn("col2", chunked: stringHolder)
+ let intHolder = ArrowArrayHolder(try uint8Builder.finish())
+ let stringHolder = ArrowArrayHolder(try stringBuilder.finish())
+ let result = RecordBatch.Builder()
+ .addColumn("col1", arrowArray: intHolder)
+ .addColumn("col2", arrowArray: stringHolder)
.finish()
-
- let schema = recordBatch.schema
- XCTAssertEqual(schema.fields.count, 2)
- XCTAssertEqual(schema.fields[0].name, "col1")
- XCTAssertEqual(schema.fields[0].type, ArrowType.ArrowUInt8)
- XCTAssertEqual(schema.fields[0].isNullable, false)
- XCTAssertEqual(schema.fields[1].name, "col2")
- XCTAssertEqual(schema.fields[1].type, ArrowType.ArrowString)
- XCTAssertEqual(schema.fields[1].isNullable, false)
- XCTAssertEqual(recordBatch.columns.count, 2)
- let col1: ChunkedArray<UInt8> = recordBatch.data(for: 0);
- let col2: ChunkedArray<String> = recordBatch.data(for: 1);
- XCTAssertEqual(col1.length, 2)
- XCTAssertEqual(col2.length, 2)
+ switch result {
+ case .success(let recordBatch):
+ let schema = recordBatch.schema
+ XCTAssertEqual(schema.fields.count, 2)
+ XCTAssertEqual(schema.fields[0].name, "col1")
+ XCTAssertEqual(schema.fields[0].type, ArrowType.ArrowUInt8)
+ XCTAssertEqual(schema.fields[0].isNullable, false)
+ XCTAssertEqual(schema.fields[1].name, "col2")
+ XCTAssertEqual(schema.fields[1].type, ArrowType.ArrowString)
+ XCTAssertEqual(schema.fields[1].isNullable, false)
+ XCTAssertEqual(recordBatch.columns.count, 2)
+ let col1: ArrowArray<UInt8> = recordBatch.data(for: 0);
+ let col2: ArrowArray<String> = recordBatch.data(for: 1);
+ XCTAssertEqual(col1.length, 2)
+ XCTAssertEqual(col2.length, 2)
+ case .failure(let error):
+ throw error
+ }
}
}
diff --git a/swift/Arrow/Tests/ArrowTests/TableTests.swift
b/swift/Arrow/Tests/ArrowTests/TableTests.swift
index 2d0a48e2d6..a3ed87d321 100644
--- a/swift/Arrow/Tests/ArrowTests/TableTests.swift
+++ b/swift/Arrow/Tests/ArrowTests/TableTests.swift
@@ -71,33 +71,96 @@ final class TableTests: XCTestCase {
XCTAssertEqual(col2.length, 2)
XCTAssertEqual(col3.length, 2)
}
-
- func testTableToRecordBatch() throws {
+
+ func testTableWithChunkedData() throws {
let uint8Builder: NumberArrayBuilder<UInt8> = try
ArrowArrayBuilders.loadNumberArrayBuilder();
uint8Builder.append(10)
uint8Builder.append(22)
+ let uint8Builder2: NumberArrayBuilder<UInt8> = try
ArrowArrayBuilders.loadNumberArrayBuilder();
+ uint8Builder2.append(33)
+ let uint8Builder3: NumberArrayBuilder<UInt8> = try
ArrowArrayBuilders.loadNumberArrayBuilder();
+ uint8Builder3.append(44)
let stringBuilder = try ArrowArrayBuilders.loadStringArrayBuilder();
stringBuilder.append("test10")
stringBuilder.append("test22")
+ let stringBuilder2 = try ArrowArrayBuilders.loadStringArrayBuilder();
+ stringBuilder.append("test33")
+ stringBuilder.append("test44")
- let table = try ArrowTable.Builder()
- .addColumn("col1", arrowArray: uint8Builder.finish())
- .addColumn("col2", arrowArray: stringBuilder.finish())
- .finish();
+ let date32Builder: Date32ArrayBuilder = try
ArrowArrayBuilders.loadDate32ArrayBuilder();
+ let date2 = Date(timeIntervalSinceReferenceDate: 86400 * 1)
+ let date1 = Date(timeIntervalSinceReferenceDate: 86400 * 5000 + 352)
+ date32Builder.append(date1)
+ date32Builder.append(date2)
+ date32Builder.append(date1)
+ date32Builder.append(date2)
- let recordBatch = table.toRecordBatch()
- let schema = recordBatch.schema
- XCTAssertEqual(schema.fields.count, 2)
+ let intArray = try ChunkedArray([uint8Builder.finish(),
uint8Builder2.finish(), uint8Builder3.finish()])
+ let stringArray = try ChunkedArray([stringBuilder.finish(),
stringBuilder2.finish()])
+ let dateArray = try ChunkedArray([date32Builder.finish()])
+ let table = ArrowTable.Builder()
+ .addColumn("col1", chunked: intArray)
+ .addColumn("col2", chunked: stringArray)
+ .addColumn("col3", chunked: dateArray)
+ .finish();
+
+ let schema = table.schema
+ XCTAssertEqual(schema.fields.count, 3)
XCTAssertEqual(schema.fields[0].name, "col1")
XCTAssertEqual(schema.fields[0].type, ArrowType.ArrowUInt8)
XCTAssertEqual(schema.fields[0].isNullable, false)
XCTAssertEqual(schema.fields[1].name, "col2")
XCTAssertEqual(schema.fields[1].type, ArrowType.ArrowString)
XCTAssertEqual(schema.fields[1].isNullable, false)
- XCTAssertEqual(recordBatch.columns.count, 2)
- let col1: ChunkedArray<UInt8> = recordBatch.data(for: 0);
- let col2: ChunkedArray<String> = recordBatch.data(for: 1);
- XCTAssertEqual(col1.length, 2)
- XCTAssertEqual(col2.length, 2)
+ XCTAssertEqual(schema.fields[1].name, "col2")
+ XCTAssertEqual(schema.fields[1].type, ArrowType.ArrowString)
+ XCTAssertEqual(schema.fields[1].isNullable, false)
+ XCTAssertEqual(table.columns.count, 3)
+ let col1: ChunkedArray<UInt8> = table.columns[0].data();
+ let col2: ChunkedArray<String> = table.columns[1].data();
+ let col3: ChunkedArray<Date> = table.columns[2].data();
+ XCTAssertEqual(col1.length, 4)
+ XCTAssertEqual(col2.length, 4)
+ XCTAssertEqual(col3.length, 4)
+ XCTAssertEqual(col1.asString(0), "10")
+ XCTAssertEqual(col1.asString(3), "44")
+ XCTAssertEqual(col2.asString(0), "test10")
+ XCTAssertEqual(col2.asString(2), "test33")
+ }
+
+ func testTableToRecordBatch() throws {
+ let uint8Builder: NumberArrayBuilder<UInt8> = try
ArrowArrayBuilders.loadNumberArrayBuilder();
+ uint8Builder.append(10)
+ uint8Builder.append(22)
+ let stringBuilder = try ArrowArrayBuilders.loadStringArrayBuilder();
+ stringBuilder.append("test10")
+ stringBuilder.append("test22")
+
+ let intHolder = ArrowArrayHolder(try uint8Builder.finish())
+ let stringHolder = ArrowArrayHolder(try stringBuilder.finish())
+ let result = RecordBatch.Builder()
+ .addColumn("col1", arrowArray: intHolder)
+ .addColumn("col2", arrowArray: stringHolder)
+ .finish().flatMap({ rb in
+ return ArrowTable.from(recordBatches: [rb])
+ })
+ switch result {
+ case .success(let table):
+ let schema = table.schema
+ XCTAssertEqual(schema.fields.count, 2)
+ XCTAssertEqual(schema.fields[0].name, "col1")
+ XCTAssertEqual(schema.fields[0].type, ArrowType.ArrowUInt8)
+ XCTAssertEqual(schema.fields[0].isNullable, false)
+ XCTAssertEqual(schema.fields[1].name, "col2")
+ XCTAssertEqual(schema.fields[1].type, ArrowType.ArrowString)
+ XCTAssertEqual(schema.fields[1].isNullable, false)
+ XCTAssertEqual(table.columns.count, 2)
+ let col1: ChunkedArray<UInt8> = table.columns[0].data();
+ let col2: ChunkedArray<String> = table.columns[1].data();
+ XCTAssertEqual(col1.length, 2)
+ XCTAssertEqual(col2.length, 2)
+ case .failure(let error):
+ throw error
+ }
}
}