should be it
This commit is contained in:
297
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/CTypeUtilities.swift
vendored
Normal file
297
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/CTypeUtilities.swift
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
@_implementationOnly import Cduckdb
|
||||
import Foundation
|
||||
|
||||
// MARK: - Type Layouts
|
||||
|
||||
struct duckdb_list_entry_t {
|
||||
let offset: UInt64
|
||||
let length: UInt64
|
||||
}
|
||||
|
||||
extension duckdb_state {
|
||||
static let success = duckdb_state(0)
|
||||
static let failure = duckdb_state(1)
|
||||
}
|
||||
|
||||
extension duckdb_pending_state {
|
||||
static let ready = duckdb_pending_state(0)
|
||||
static let notReady = duckdb_pending_state(1)
|
||||
static let error = duckdb_pending_state(2)
|
||||
}
|
||||
|
||||
// MARK: - Type ID
|
||||
|
||||
extension duckdb_type {
|
||||
var asTypeID: DatabaseType { .init(rawValue: rawValue) }
|
||||
}
|
||||
|
||||
// MARK: - String
|
||||
|
||||
extension duckdb_string {
|
||||
|
||||
private static let inlineLimit = UInt32(12)
|
||||
|
||||
var asString: String {
|
||||
withUnsafePointer(to: self) { ptr in
|
||||
let contentsSize = UnsafeRawPointer(ptr).load(as: UInt32.self)
|
||||
let strPtr: UnsafeRawPointer
|
||||
if contentsSize <= Self.inlineLimit {
|
||||
strPtr = UnsafeRawPointer(ptr).advanced(by: MemoryLayout<UInt32>.stride)
|
||||
}
|
||||
else {
|
||||
let strPtrPtr = UnsafeRawPointer(ptr).advanced(by: MemoryLayout<UInt64>.stride)
|
||||
strPtr = strPtrPtr.load(as: UnsafeRawPointer.self)
|
||||
}
|
||||
let stringData = Data(bytes: strPtr, count: Int(contentsSize))
|
||||
return String(data: stringData, encoding:.utf8)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Huge Int
|
||||
|
||||
extension duckdb_hugeint {
|
||||
|
||||
init(_ source: IntHuge) {
|
||||
self = duckdb_hugeint(lower: source.low, upper: source.high)
|
||||
}
|
||||
|
||||
var asIntHuge: IntHuge { .init(high: upper, low: lower) }
|
||||
|
||||
var asUIntHuge: UIntHuge {
|
||||
let high = IntHuge(upper) + IntHuge(Int64.max) + 1
|
||||
return UIntHuge(high) << 64 + UIntHuge(lower)
|
||||
}
|
||||
|
||||
var asUUID: UUID {
|
||||
let value = asUIntHuge
|
||||
let uuid = UUID(
|
||||
uuid: uuid_t(
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 15)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 14)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 13)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 12)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 11)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 10)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 9)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 8)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 7)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 6)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 5)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 4)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 3)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 2)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 1)),
|
||||
UInt8(truncatingIfNeeded: value >> (8 * 0))
|
||||
)
|
||||
)
|
||||
return uuid
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Unsigned Huge Int
|
||||
|
||||
extension duckdb_uhugeint {
|
||||
|
||||
init(_ source: UIntHuge) {
|
||||
self = duckdb_uhugeint(lower: source.low, upper: source.high)
|
||||
}
|
||||
|
||||
var asUIntHuge: UIntHuge { .init(high: upper, low: lower) }
|
||||
}
|
||||
|
||||
// MARK: - Time
|
||||
|
||||
extension duckdb_time {
|
||||
init(time: Time) { self = duckdb_time(micros: time.microseconds) }
|
||||
var asTime: Time { Time(microseconds: micros) }
|
||||
}
|
||||
|
||||
extension duckdb_time_struct {
|
||||
|
||||
init(components: Time.Components) {
|
||||
self = duckdb_time_struct(
|
||||
hour: components.hour,
|
||||
min: components.minute,
|
||||
sec: components.second,
|
||||
micros: components.microsecond
|
||||
)
|
||||
}
|
||||
|
||||
var asTimeComponents: Time.Components {
|
||||
.init(hour: hour, minute: min, second: sec, microsecond: micros)
|
||||
}
|
||||
}
|
||||
|
||||
extension duckdb_time_tz {
|
||||
var asTime: TimeTz {
|
||||
let res = duckdb_from_time_tz(self)
|
||||
let ctimestruct = duckdb_to_time(res.time);
|
||||
return TimeTz(time: Time(microseconds: ctimestruct.micros), offset: res.offset)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Date
|
||||
|
||||
extension duckdb_date {
|
||||
init(date: Date) { self = duckdb_date(days: date.days) }
|
||||
var asDate: Date { Date(days: days) }
|
||||
}
|
||||
|
||||
extension duckdb_date_struct {
|
||||
|
||||
init(components: Date.Components) {
|
||||
self = duckdb_date_struct(year: components.year, month: components.month, day: components.day)
|
||||
}
|
||||
|
||||
var asDateComponents: Date.Components {
|
||||
.init(year: year, month: month, day: day)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Timestamp
|
||||
|
||||
extension duckdb_timestamp {
|
||||
init(timestamp: Timestamp) { self = duckdb_timestamp(micros: timestamp.microseconds) }
|
||||
var asTimestamp: Timestamp { Timestamp(microseconds: micros) }
|
||||
}
|
||||
|
||||
extension duckdb_timestamp_struct {
|
||||
|
||||
init(components: Timestamp.Components) {
|
||||
self = duckdb_timestamp_struct(
|
||||
date: duckdb_date_struct(components: components.date),
|
||||
time: duckdb_time_struct(components: components.time)
|
||||
)
|
||||
}
|
||||
|
||||
var asTimestampComponents: Timestamp.Components {
|
||||
.init(date: date.asDateComponents, time: time.asTimeComponents)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interval
|
||||
|
||||
extension duckdb_interval {
|
||||
|
||||
init(interval: Interval) {
|
||||
self = duckdb_interval(
|
||||
months: interval.months, days: interval.days, micros: interval.microseconds)
|
||||
}
|
||||
|
||||
var asInterval: Interval {
|
||||
Interval(months: months, days: days, microseconds: micros)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Blob
|
||||
|
||||
extension duckdb_blob {
|
||||
|
||||
private static let inlineLimit = UInt32(12)
|
||||
|
||||
var asData: Data {
|
||||
withUnsafePointer(to: self) { ptr in
|
||||
let contentsSize = UnsafeRawPointer(ptr).load(as: UInt32.self)
|
||||
let blobPtr: UnsafeRawPointer
|
||||
if contentsSize <= Self.inlineLimit {
|
||||
blobPtr = UnsafeRawPointer(ptr).advanced(by: MemoryLayout<UInt32>.stride)
|
||||
}
|
||||
else {
|
||||
let blobPtrPtr = UnsafeRawPointer(ptr).advanced(by: MemoryLayout<UInt64>.stride)
|
||||
blobPtr = blobPtrPtr.load(as: UnsafeRawPointer.self)
|
||||
}
|
||||
return Data(bytes: blobPtr, count: Int(contentsSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Decimal
|
||||
|
||||
extension duckdb_decimal {
|
||||
|
||||
private static let scaleLimit = 38
|
||||
|
||||
init(_ source: Decimal) throws {
|
||||
let mantissaLimit = source.isSignMinus ? 1 + UIntHuge(IntHuge.max) : UIntHuge(IntHuge.max)
|
||||
var scale: Int
|
||||
var mantissa: UIntHuge
|
||||
if source.exponent > 0 {
|
||||
let exponent = UIntHuge(source.exponent)
|
||||
let (out, overflow) = source.hugeMantissa.multipliedReportingOverflow(by: exponent)
|
||||
guard overflow == false else { throw DatabaseError.decimalUnrepresentable }
|
||||
mantissa = out
|
||||
scale = 0
|
||||
}
|
||||
else {
|
||||
scale = -source.exponent
|
||||
mantissa = source.hugeMantissa
|
||||
while scale > Self.scaleLimit {
|
||||
mantissa /= 10
|
||||
scale -= 1
|
||||
}
|
||||
while scale > 0, mantissa > mantissaLimit {
|
||||
mantissa /= 10
|
||||
scale -= 1
|
||||
}
|
||||
}
|
||||
guard mantissa <= mantissaLimit else {
|
||||
throw DatabaseError.decimalUnrepresentable
|
||||
}
|
||||
guard mantissa > 0 else {
|
||||
self = duckdb_decimal(width: 0, scale: 0, value: .init(lower: 0, upper: 0))
|
||||
return
|
||||
}
|
||||
let value = source.isSignMinus
|
||||
? IntHuge.min + IntHuge(mantissaLimit - mantissa) : IntHuge(mantissa)
|
||||
self = duckdb_decimal(width: 38, scale: .init(scale), value: .init(value))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension Decimal {
|
||||
|
||||
var hugeMantissa: UIntHuge {
|
||||
let components = [
|
||||
_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3,
|
||||
_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7
|
||||
]
|
||||
var mantissa = UIntHuge(0)
|
||||
for i in 0..<Int(_length) {
|
||||
mantissa += UIntHuge(components[i]) << (i * 16)
|
||||
}
|
||||
return mantissa
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Vector
|
||||
|
||||
extension duckdb_vector {
|
||||
|
||||
var logicalType: LogicalType {
|
||||
LogicalType { duckdb_vector_get_column_type(self) }
|
||||
}
|
||||
}
|
||||
47
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/DataChunk.swift
vendored
Normal file
47
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/DataChunk.swift
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
@_implementationOnly import Cduckdb
|
||||
import Foundation
|
||||
|
||||
final class DataChunk: Sendable {
|
||||
|
||||
var count: DBInt { duckdb_data_chunk_get_size(ptr.pointee) }
|
||||
var columnCount: DBInt { duckdb_data_chunk_get_column_count(ptr.pointee) }
|
||||
|
||||
private let ptr = UnsafeMutablePointer<duckdb_data_chunk?>.allocate(capacity: 1)
|
||||
|
||||
init(cresult: duckdb_result, index: DBInt) {
|
||||
self.ptr.pointee = duckdb_result_get_chunk(cresult, index)!
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_destroy_data_chunk(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
func withVector<T>(at index: DBInt, _ body: (Vector) throws -> T) rethrows -> T {
|
||||
try body(Vector(duckdb_data_chunk_get_vector(ptr.pointee, index), count: Int(count)))
|
||||
}
|
||||
}
|
||||
48
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/DecimalStorageType.swift
vendored
Normal file
48
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/DecimalStorageType.swift
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol DecimalStorageType {
|
||||
var asDecimal: Decimal { get }
|
||||
}
|
||||
|
||||
extension Int8: DecimalStorageType {
|
||||
var asDecimal: Decimal { Decimal(self) }
|
||||
}
|
||||
|
||||
extension Int16: DecimalStorageType {
|
||||
var asDecimal: Decimal { Decimal(self) }
|
||||
}
|
||||
extension Int32: DecimalStorageType {
|
||||
var asDecimal: Decimal { Decimal(self) }
|
||||
}
|
||||
|
||||
extension Int64: DecimalStorageType {
|
||||
var asDecimal: Decimal { Decimal(self) }
|
||||
}
|
||||
|
||||
extension IntHuge: DecimalStorageType {
|
||||
var asDecimal: Decimal { Decimal(self) }
|
||||
}
|
||||
33
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/Optional+CString.swift
vendored
Normal file
33
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/Optional+CString.swift
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Optional where Wrapped: StringProtocol {
|
||||
|
||||
func withOptionalCString<T>(_ body: (UnsafePointer<CChar>?) throws -> T) rethrows -> T {
|
||||
guard let string = self else { return try body(nil) }
|
||||
return try string.withCString(body)
|
||||
}
|
||||
}
|
||||
71
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/PrimitiveDatabaseValue.swift
vendored
Normal file
71
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/PrimitiveDatabaseValue.swift
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
protocol PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { get }
|
||||
}
|
||||
|
||||
extension Bool: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .boolean }
|
||||
}
|
||||
|
||||
extension Int8: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .tinyint }
|
||||
}
|
||||
|
||||
extension Int16: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .smallint }
|
||||
}
|
||||
|
||||
extension Int32: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .integer }
|
||||
}
|
||||
|
||||
extension Int64: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .bigint }
|
||||
}
|
||||
|
||||
extension UInt8: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .utinyint }
|
||||
}
|
||||
|
||||
extension UInt16: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .usmallint }
|
||||
}
|
||||
|
||||
extension UInt32: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .uinteger }
|
||||
}
|
||||
|
||||
extension UInt64: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .ubigint }
|
||||
}
|
||||
|
||||
extension Float: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .float }
|
||||
}
|
||||
|
||||
extension Double: PrimitiveDatabaseValue {
|
||||
static var representedDatabaseTypeID: DatabaseType { .double }
|
||||
}
|
||||
340
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/Vector.swift
vendored
Normal file
340
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/Vector.swift
vendored
Normal file
@@ -0,0 +1,340 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
@_implementationOnly import Cduckdb
|
||||
import Foundation
|
||||
|
||||
struct Vector {
|
||||
|
||||
static let vectorSize = DBInt(duckdb_vector_size())
|
||||
|
||||
let count: Int
|
||||
let offset: Int
|
||||
let logicalType: LogicalType
|
||||
private let cvector: duckdb_vector
|
||||
|
||||
init(_ cvector: duckdb_vector, count: Int, offset: Int = 0, logicalType: LogicalType? = nil) {
|
||||
self.count = count
|
||||
self.offset = offset
|
||||
self.cvector = cvector
|
||||
self.logicalType = logicalType ?? cvector.logicalType
|
||||
}
|
||||
|
||||
func withCVector<T>(_ body: (duckdb_vector) throws -> T) rethrows -> T {
|
||||
try body(cvector)
|
||||
}
|
||||
}
|
||||
|
||||
extension Vector {
|
||||
|
||||
func unwrapNull(at index: Int) -> Bool {
|
||||
precondition(index < count, "vector index out of bounds")
|
||||
let offsetIndex = offset + index
|
||||
let validityMasksPtr = duckdb_vector_get_validity(cvector)
|
||||
guard let validityMasksPtr else { return false }
|
||||
let validityMaskEntryIndex = offsetIndex / 64
|
||||
let validityMaskEntryPtr = (validityMasksPtr + validityMaskEntryIndex)
|
||||
let validityBitIndex = offsetIndex % 64
|
||||
let validityBit = (DBInt(1) << validityBitIndex)
|
||||
return validityMaskEntryPtr.pointee & validityBit == 0
|
||||
}
|
||||
|
||||
func unwrap(_ type: Int.Type, at index: Int) throws -> Int {
|
||||
switch logicalType.dataType {
|
||||
case .tinyint:
|
||||
return Int(try unwrap(Int8.self, at: index))
|
||||
case .smallint:
|
||||
return Int(try unwrap(Int16.self, at: index))
|
||||
case .integer:
|
||||
return Int(try unwrap(Int32.self, at: index))
|
||||
case .bigint:
|
||||
return Int(try unwrap(Int64.self, at: index))
|
||||
default:
|
||||
throw DatabaseError.typeMismatch(Int.self)
|
||||
}
|
||||
}
|
||||
|
||||
func unwrap(_ type: UInt.Type, at index: Int) throws -> UInt {
|
||||
switch logicalType.dataType {
|
||||
case .utinyint:
|
||||
return UInt(try unwrap(UInt8.self, at: index))
|
||||
case .usmallint:
|
||||
return UInt(try unwrap(UInt16.self, at: index))
|
||||
case .uinteger:
|
||||
return UInt(try unwrap(UInt32.self, at: index))
|
||||
case .ubigint:
|
||||
return UInt(try unwrap(UInt64.self, at: index))
|
||||
default:
|
||||
throw DatabaseError.typeMismatch(Int.self)
|
||||
}
|
||||
}
|
||||
|
||||
func unwrap<T: PrimitiveDatabaseValue>(_ type: T.Type, at index: Int) throws -> T {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: T.representedDatabaseTypeID)
|
||||
return unsafelyUnwrapElement(as: T.self, at: index)
|
||||
}
|
||||
|
||||
func unwrap(_ type: String.Type, at index: Int) throws -> String {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .varchar)
|
||||
return unsafelyUnwrapElement(as: duckdb_string.self, at: index) { $0.asString }
|
||||
}
|
||||
|
||||
func unwrap(_ type: IntHuge.Type, at index: Int) throws -> IntHuge {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .hugeint)
|
||||
return unsafelyUnwrapElement(as: duckdb_hugeint.self, at: index) { $0.asIntHuge }
|
||||
}
|
||||
|
||||
func unwrap(_ type: UIntHuge.Type, at index: Int) throws -> UIntHuge {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .uhugeint)
|
||||
return unsafelyUnwrapElement(as: duckdb_uhugeint.self, at: index) { $0.asUIntHuge }
|
||||
}
|
||||
|
||||
func unwrap(_ type: UUID.Type, at index: Int) throws -> UUID {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .uuid)
|
||||
return unsafelyUnwrapElement(as: duckdb_hugeint.self, at: index) { $0.asUUID }
|
||||
}
|
||||
|
||||
func unwrap(_ type: Time.Type, at index: Int) throws -> Time {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .time)
|
||||
return unsafelyUnwrapElement(as: duckdb_time.self, at: index) { $0.asTime }
|
||||
}
|
||||
|
||||
func unwrap(_ type: TimeTz.Type, at index: Int) throws -> TimeTz {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .timeTz)
|
||||
return unsafelyUnwrapElement(as: duckdb_time_tz.self, at: index) { $0.asTime }
|
||||
}
|
||||
|
||||
func unwrap(_ type: Date.Type, at index: Int) throws -> Date {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .date)
|
||||
return unsafelyUnwrapElement(as: duckdb_date.self, at: index) { $0.asDate }
|
||||
}
|
||||
|
||||
func unwrap(_ type: Interval.Type, at index: Int) throws -> Interval {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .interval)
|
||||
return unsafelyUnwrapElement(as: duckdb_interval.self, at: index) { $0.asInterval }
|
||||
}
|
||||
|
||||
func unwrap(_ type: Timestamp.Type, at index: Int) throws -> Timestamp {
|
||||
let columnTypes = [DatabaseType.timestampS, .timestampMS, .timestamp, .timestampTz, .timestampNS]
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnTypes: .init(columnTypes))
|
||||
return unsafelyUnwrapElement(as: duckdb_timestamp.self, at: index) { ctimestamp in
|
||||
switch logicalType.dataType {
|
||||
case .timestampS:
|
||||
let scaled = duckdb_timestamp(micros: ctimestamp.micros * 1_000_000)
|
||||
return scaled.asTimestamp
|
||||
case .timestampMS:
|
||||
let scaled = duckdb_timestamp(micros: ctimestamp.micros * 1_000)
|
||||
return scaled.asTimestamp
|
||||
case .timestampNS:
|
||||
let scaled = duckdb_timestamp(micros: ctimestamp.micros / 1_000)
|
||||
return scaled.asTimestamp
|
||||
default:
|
||||
return ctimestamp.asTimestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unwrap(_ type: Data.Type, at index: Int) throws -> Data {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .blob)
|
||||
return unsafelyUnwrapElement(as: duckdb_blob.self, at: index) { $0.asData }
|
||||
}
|
||||
|
||||
func unwrap(_ type: Decimal.Type, at index: Int) throws -> Decimal {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnType: .decimal)
|
||||
guard let props = logicalType.decimalProperties else {
|
||||
fatalError("expected decimal logical type")
|
||||
}
|
||||
switch props.storageType {
|
||||
case .tinyint:
|
||||
return unwrapDecimal(withUnderlyingType: Int8.self, scale: props.scale, at: index)
|
||||
case .smallint:
|
||||
return unwrapDecimal(withUnderlyingType: Int16.self, scale: props.scale, at: index)
|
||||
case .integer:
|
||||
return unwrapDecimal(withUnderlyingType: Int32.self, scale: props.scale, at: index)
|
||||
case .bigint:
|
||||
return unwrapDecimal(withUnderlyingType: Int64.self, scale: props.scale, at: index)
|
||||
case .hugeint:
|
||||
return unwrapDecimal(withUnderlyingType: IntHuge.self, scale: props.scale, at: index)
|
||||
case let unexpectedInternalType:
|
||||
fatalError("unexpected internal decimal type: \(unexpectedInternalType)")
|
||||
}
|
||||
}
|
||||
|
||||
func unwrapDecimal<T: DecimalStorageType>(
|
||||
withUnderlyingType storageType: T.Type, scale: UInt8, at index: Int
|
||||
) -> Decimal {
|
||||
unsafelyUnwrapElement(as: T.self, at: index) { decimalStorage in
|
||||
let storageValue = decimalStorage.asDecimal
|
||||
let sign = storageValue.sign
|
||||
let exponent = -Int(scale)
|
||||
let significand = abs(storageValue)
|
||||
return Decimal(sign: sign, exponent: exponent, significand: significand)
|
||||
}
|
||||
}
|
||||
|
||||
func unsafelyUnwrapElement<T>(as type: T.Type, at index: Int) -> T {
|
||||
unsafelyUnwrapElement(as: type, at: index) { $0 }
|
||||
}
|
||||
|
||||
func unsafelyUnwrapElement<T, U>(
|
||||
as type: T.Type, at index: Int, transform: (T) -> U
|
||||
) -> U {
|
||||
precondition(index < count, "vector index out of bounds")
|
||||
let offsetIndex = offset + index
|
||||
let dataPtr = duckdb_vector_get_data(cvector)!
|
||||
let itemDataPtr = dataPtr.assumingMemoryBound(to: T.self)
|
||||
return transform(itemDataPtr.advanced(by: offsetIndex)[0])
|
||||
}
|
||||
|
||||
func assertNonNullTypeMatch<T>(
|
||||
of type: T.Type, at index: Int, withColumnType columnType: DatabaseType
|
||||
) throws {
|
||||
try assertNonNullTypeMatch(of: type, at: index, withColumnTypes: .init([columnType]))
|
||||
}
|
||||
|
||||
func assertNonNullTypeMatch<T>(
|
||||
of type: T.Type, at index: Int, withColumnTypes columnTypes: Set<DatabaseType>
|
||||
) throws {
|
||||
guard unwrapNull(at: index) == false else {
|
||||
throw DatabaseError.valueNotFound(type)
|
||||
}
|
||||
guard columnTypes.contains(logicalType.underlyingDataType) else {
|
||||
throw DatabaseError.typeMismatch(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Collection Conformance
|
||||
|
||||
extension Vector: Collection {
|
||||
|
||||
struct Element {
|
||||
let vector: Vector
|
||||
let index: Int
|
||||
}
|
||||
|
||||
public struct Iterator: IteratorProtocol {
|
||||
|
||||
private let vector: Vector
|
||||
private var position: Int
|
||||
|
||||
init(_ vector: Vector) {
|
||||
self.vector = vector
|
||||
self.position = 0
|
||||
}
|
||||
|
||||
public mutating func next() -> Element? {
|
||||
guard position < vector.count else { return nil }
|
||||
defer { position += 1 }
|
||||
return vector[position]
|
||||
}
|
||||
}
|
||||
|
||||
public var startIndex: Int { 0 }
|
||||
public var endIndex: Int { count }
|
||||
|
||||
public subscript(position: Int) -> Element { Element(vector: self, index: position) }
|
||||
public func makeIterator() -> Iterator { Iterator(self) }
|
||||
|
||||
public func index(after i: Int) -> Int { i + 1 }
|
||||
public func index(before i: Int) -> Int { i - 1 }
|
||||
}
|
||||
|
||||
// MARK: - Element Accessors
|
||||
|
||||
extension Vector.Element {
|
||||
|
||||
var dataType: DatabaseType { vector.logicalType.dataType }
|
||||
var logicalType: LogicalType { vector.logicalType }
|
||||
|
||||
func unwrapNull() -> Bool { vector.unwrapNull(at: index) }
|
||||
func unwrap(_ type: Int.Type) throws -> Int { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: UInt.Type) throws -> UInt { try vector.unwrap(type, at: index) }
|
||||
func unwrap<T: PrimitiveDatabaseValue>(_ type: T.Type) throws -> T { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: String.Type) throws -> String { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: IntHuge.Type) throws -> IntHuge { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: UIntHuge.Type) throws -> UIntHuge { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: UUID.Type) throws -> UUID { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: Time.Type) throws -> Time { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: Date.Type) throws -> Date { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: Interval.Type) throws -> Interval { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: Timestamp.Type) throws -> Timestamp { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: Data.Type) throws -> Data { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: Decimal.Type) throws -> Decimal { try vector.unwrap(type, at: index) }
|
||||
func unwrap(_ type: TimeTz.Type) throws -> TimeTz { try vector.unwrap(type, at: index) }
|
||||
func unwrapDecodable<T: Decodable>(_ type: T.Type) throws -> T {
|
||||
try VectorElementDecoder.default.decode(T.self, element: self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Map Contents accessors
|
||||
|
||||
extension Vector.Element {
|
||||
|
||||
struct MapContent {
|
||||
let keyVector: Vector
|
||||
let valueVector: Vector
|
||||
}
|
||||
|
||||
var childVector: Vector? {
|
||||
guard let child = duckdb_list_vector_get_child(vector.cvector) else { return nil }
|
||||
let count = duckdb_list_vector_get_size(vector.cvector)
|
||||
let info = vector.unsafelyUnwrapElement(as: duckdb_list_entry_t.self, at: vector.offset + index)
|
||||
precondition(info.offset + info.length <= count)
|
||||
return Vector(child, count: Int(info.length), offset: Int(info.offset))
|
||||
}
|
||||
|
||||
var mapContents: MapContent? {
|
||||
guard dataType == .map else { return nil }
|
||||
guard let childVector = childVector else { return nil }
|
||||
guard let keys = duckdb_struct_vector_get_child(childVector.cvector, 0) else { return nil }
|
||||
guard let values = duckdb_struct_vector_get_child(childVector.cvector, 1) else { return nil }
|
||||
let keyVector = Vector(keys, count: childVector.count, offset: childVector.offset)
|
||||
let valueVector = Vector(values, count: childVector.count, offset: childVector.offset)
|
||||
return MapContent(keyVector: keyVector, valueVector: valueVector)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Struct Contents accesors
|
||||
|
||||
extension Vector.Element {
|
||||
|
||||
struct StructMemberContent {
|
||||
let name: String
|
||||
let vector: Vector
|
||||
}
|
||||
|
||||
var structContents: [StructMemberContent]? {
|
||||
guard let properties = vector.logicalType.structMemberProperties else { return nil }
|
||||
var content = [StructMemberContent]()
|
||||
for (i, member) in properties.enumerated() {
|
||||
let memberCVector = duckdb_struct_vector_get_child(vector.cvector, DBInt(i))!
|
||||
let memberVector = Vector(memberCVector, count: vector.count, offset: vector.offset)
|
||||
content.append(.init(name: member.name, vector: memberVector))
|
||||
}
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
547
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/VectorElementDecoder.swift
vendored
Normal file
547
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Internal/VectorElementDecoder.swift
vendored
Normal file
@@ -0,0 +1,547 @@
|
||||
//
|
||||
// DuckDB
|
||||
// https://github.com/duckdb/duckdb-swift
|
||||
//
|
||||
// Copyright © 2018-2024 Stichting DuckDB Foundation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
@_implementationOnly import Cduckdb
|
||||
import Foundation
|
||||
|
||||
// MARK: - Top Level Decoder
|
||||
|
||||
final class VectorElementDecoder {
|
||||
|
||||
static let `default` = VectorElementDecoder()
|
||||
|
||||
func decode<T: Decodable>(_ type: T.Type, element: Vector.Element) throws -> T {
|
||||
try T(from: VectorElementDataDecoder(element: element))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Vector Decoder
|
||||
|
||||
fileprivate struct VectorElementDataDecoder: Decoder {
|
||||
|
||||
struct VectorDecoderCodingKey: CodingKey {
|
||||
|
||||
let stringValue: String
|
||||
let intValue: Int?
|
||||
|
||||
init(stringValue: String) {
|
||||
self.intValue = nil
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
init(intValue: Int) {
|
||||
self.intValue = intValue
|
||||
self.stringValue = "\(intValue)"
|
||||
}
|
||||
}
|
||||
|
||||
let codingPath: [CodingKey]
|
||||
let element: Vector.Element
|
||||
let userInfo: [CodingUserInfoKey : Any]
|
||||
|
||||
init(element: Vector.Element, codingPath: [CodingKey] = []) {
|
||||
self.codingPath = codingPath
|
||||
self.element = element
|
||||
self.userInfo = [CodingUserInfoKeys.logicalTypeCodingUserInfoKey: element.logicalType]
|
||||
}
|
||||
|
||||
func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
|
||||
switch element.dataType {
|
||||
case .map:
|
||||
return .init(
|
||||
try KeyedValueContainer<Key>.createMapContainer(decoder: self, element: element)
|
||||
)
|
||||
case .struct:
|
||||
return .init(
|
||||
try KeyedValueContainer<Key>.createStructContainer(decoder: self, element: element)
|
||||
)
|
||||
default:
|
||||
let columnType = element.dataType
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Cannot create keyed decoding container for column type \(columnType)"
|
||||
)
|
||||
throw DecodingError.typeMismatch(type, context)
|
||||
}
|
||||
}
|
||||
|
||||
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
switch element.dataType {
|
||||
case .list:
|
||||
return try UnkeyedValueContainer.createListContainer(decoder: self, element: element)
|
||||
default:
|
||||
let columnType = element.dataType
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Cannot create unkeyed decoding container for column type \(columnType)"
|
||||
)
|
||||
throw DecodingError.typeMismatch(UnkeyedDecodingContainer.self, context)
|
||||
}
|
||||
}
|
||||
|
||||
func singleValueContainer() -> SingleValueDecodingContainer {
|
||||
SingleValueContainer(decoder: self, codingPath: codingPath, element: element)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Single Value Container
|
||||
|
||||
extension VectorElementDataDecoder {
|
||||
|
||||
struct SingleValueContainer: SingleValueDecodingContainer {
|
||||
|
||||
let decoder: VectorElementDataDecoder
|
||||
var codingPath: [CodingKey]
|
||||
let element: Vector.Element
|
||||
|
||||
init(decoder: VectorElementDataDecoder, codingPath: [CodingKey], element: Vector.Element) {
|
||||
self.decoder = decoder
|
||||
self.codingPath = codingPath
|
||||
self.element = element
|
||||
}
|
||||
|
||||
// Protocol conforming `decode(_:)` implementations
|
||||
|
||||
func decodeNil() -> Bool {
|
||||
element.unwrapNull()
|
||||
}
|
||||
|
||||
func decode(_ type: Int.Type) throws -> Int {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Int8.Type) throws -> Int8 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Int16.Type) throws -> Int16 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Int32.Type) throws -> Int32 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Int64.Type) throws -> Int64 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UInt.Type) throws -> UInt {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UInt8.Type) throws -> UInt8 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UInt16.Type) throws -> UInt16 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UInt32.Type) throws -> UInt32 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UInt64.Type) throws -> UInt64 {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Float.Type) throws -> Float {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Double.Type) throws -> Double {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: String.Type) throws -> String {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
// Special case `decode(_:)` implementations
|
||||
|
||||
func decode(_ type: IntHuge.Type) throws -> IntHuge {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UIntHuge.Type) throws -> UIntHuge {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Decimal.Type) throws -> Decimal {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Data.Type) throws -> Data {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: UUID.Type) throws -> UUID {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Date.Type) throws -> Date {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Time.Type) throws -> Time {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: TimeTz.Type) throws -> TimeTz {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Timestamp.Type) throws -> Timestamp {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
func decode(_ type: Interval.Type) throws -> Interval {
|
||||
try attemptDecode { try element.unwrap(type) }
|
||||
}
|
||||
|
||||
// Generic decode
|
||||
|
||||
func decode<T: Decodable>(_ type: T.Type) throws -> T {
|
||||
switch type {
|
||||
case is IntHuge.Type:
|
||||
return try decode(IntHuge.self) as! T
|
||||
case is UIntHuge.Type:
|
||||
return try decode(UIntHuge.self) as! T
|
||||
case is Decimal.Type:
|
||||
return try decode(Decimal.self) as! T
|
||||
case is Data.Type:
|
||||
return try decode(Data.self) as! T
|
||||
case is UUID.Type:
|
||||
return try decode(UUID.self) as! T
|
||||
case is Date.Type:
|
||||
return try decode(Date.self) as! T
|
||||
case is Time.Type:
|
||||
return try decode(Time.self) as! T
|
||||
case is Timestamp.Type:
|
||||
return try decode(Timestamp.self) as! T
|
||||
case is Interval.Type:
|
||||
return try decode(Interval.self) as! T
|
||||
case is TimeTz.Type:
|
||||
return try decode(TimeTz.self) as! T
|
||||
default:
|
||||
return try T(from: decoder)
|
||||
}
|
||||
}
|
||||
|
||||
private func attemptDecode<T>(_ body: () throws -> T) throws -> T {
|
||||
do {
|
||||
return try body()
|
||||
}
|
||||
catch DatabaseError.valueNotFound(let type) {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Expected value of type \(type) – found null value instead"
|
||||
)
|
||||
throw DecodingError.valueNotFound(type, context)
|
||||
}
|
||||
catch DatabaseError.typeMismatch(let type) {
|
||||
let columnType = element.dataType
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Expected to decode \(type) but found \(columnType) instead."
|
||||
)
|
||||
throw DecodingError.typeMismatch(type, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Unkeyed Container
|
||||
|
||||
extension VectorElementDataDecoder {
|
||||
|
||||
struct UnkeyedValueContainer: UnkeyedDecodingContainer {
|
||||
|
||||
let decoder: VectorElementDataDecoder
|
||||
let codingPath: [CodingKey]
|
||||
let vector: Vector
|
||||
let count: Int?
|
||||
var currentIndex = 0
|
||||
|
||||
var isAtEnd: Bool { currentIndex >= vector.count }
|
||||
|
||||
init(
|
||||
decoder: VectorElementDataDecoder,
|
||||
codingPath: [CodingKey],
|
||||
vector: Vector
|
||||
) {
|
||||
self.decoder = decoder
|
||||
self.codingPath = codingPath
|
||||
self.vector = vector
|
||||
self.count = vector.count
|
||||
}
|
||||
|
||||
func decodeNil() -> Bool {
|
||||
vector[currentIndex].unwrapNull()
|
||||
}
|
||||
|
||||
mutating func decode<T: Decodable>(_ type: T.Type) throws -> T {
|
||||
let decoder = try superDecoder()
|
||||
let value = try T(from: decoder)
|
||||
currentIndex += 1
|
||||
return value
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey: CodingKey>(
|
||||
keyedBy type: NestedKey.Type
|
||||
) throws -> KeyedDecodingContainer<NestedKey> {
|
||||
let decoder = try superDecoder()
|
||||
return try decoder.container(keyedBy: type)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
let decoder = try superDecoder()
|
||||
return try decoder.unkeyedContainer()
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
var codingPath = decoder.codingPath
|
||||
codingPath.append(VectorDecoderCodingKey(intValue: currentIndex))
|
||||
return VectorElementDataDecoder(element: vector[currentIndex], codingPath: codingPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Keyed Container
|
||||
|
||||
extension VectorElementDataDecoder {
|
||||
|
||||
struct KeyedValueContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {
|
||||
|
||||
struct Properties {
|
||||
let keyMap: [String: KeyPath]
|
||||
let vectors: [Vector]
|
||||
}
|
||||
|
||||
struct KeyPath {
|
||||
let key: Key
|
||||
let vectorIndex: Int
|
||||
let rowIndex: Int
|
||||
}
|
||||
|
||||
let decoder: VectorElementDataDecoder
|
||||
let codingPath: [CodingKey]
|
||||
let keyMap: [String: KeyPath]
|
||||
let vectors: [Vector]
|
||||
|
||||
init(
|
||||
decoder: VectorElementDataDecoder,
|
||||
codingPath: [CodingKey],
|
||||
keyMap: [String: KeyPath],
|
||||
vectors: [Vector]
|
||||
) {
|
||||
self.decoder = decoder
|
||||
self.codingPath = codingPath
|
||||
self.keyMap = keyMap
|
||||
self.vectors = vectors
|
||||
}
|
||||
|
||||
var allKeys: [Key] { keyMap.values.map { $0.key } }
|
||||
|
||||
func contains(_ key: Key) -> Bool {
|
||||
keyMap[key.stringValue] != nil
|
||||
}
|
||||
|
||||
func decodeNil(forKey key: Key) throws -> Bool {
|
||||
let path = try unwrapPath(forKey: key)
|
||||
let vector = vectors[path.vectorIndex]
|
||||
return vector[path.rowIndex].unwrapNull()
|
||||
}
|
||||
|
||||
func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
|
||||
let decoder = try superDecoder(forKey: key)
|
||||
return try T(from: decoder)
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey: CodingKey>(
|
||||
keyedBy type: NestedKey.Type, forKey key: Key
|
||||
) throws -> KeyedDecodingContainer<NestedKey> {
|
||||
let decoder = try superDecoder(forKey: key)
|
||||
return try decoder.container(keyedBy: type)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
|
||||
let decoder = try superDecoder(forKey: key)
|
||||
return try decoder.unkeyedContainer()
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
VectorElementDataDecoder(element: decoder.element, codingPath: decoder.codingPath)
|
||||
}
|
||||
|
||||
func superDecoder(forKey key: Key) throws -> Decoder {
|
||||
let path = try unwrapPath(forKey: key)
|
||||
let vector = vectors[path.vectorIndex]
|
||||
var codingPath = decoder.codingPath
|
||||
codingPath.append(VectorDecoderCodingKey(stringValue: key.stringValue))
|
||||
return VectorElementDataDecoder(element: vector[path.rowIndex], codingPath: codingPath)
|
||||
}
|
||||
|
||||
private func unwrapPath(forKey key: Key) throws -> KeyPath {
|
||||
guard let path = keyMap[key.stringValue] else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Key not found"
|
||||
)
|
||||
throw DecodingError.keyNotFound(key, context)
|
||||
}
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Container Factories
|
||||
|
||||
fileprivate extension VectorElementDataDecoder.KeyedValueContainer {
|
||||
|
||||
static func createMapContainer(
|
||||
decoder: VectorElementDataDecoder, element: Vector.Element
|
||||
) throws -> Self {
|
||||
guard element.unwrapNull() == false else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Cannot get keyed decoding container – found null value instead"
|
||||
)
|
||||
throw DecodingError.valueNotFound(Self.self, context)
|
||||
}
|
||||
guard element.dataType == .map else {
|
||||
let columnType = element.dataType
|
||||
let context = DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Expected map column type, found \(columnType) column type instead."
|
||||
)
|
||||
throw DecodingError.typeMismatch(Self.self, context)
|
||||
}
|
||||
guard let mapContent = element.mapContents else {
|
||||
fatalError("Internal consistency error. Expected map content in vector.")
|
||||
}
|
||||
let keys: [Key]
|
||||
switch mapContent.keyVector.logicalType.dataType {
|
||||
case .varchar:
|
||||
let stringKeys = try mapContent.keyVector.map { try $0.unwrap(String.self) }
|
||||
keys = try stringKeys.map { try createKey(stringValue: $0) }
|
||||
case .tinyint, .smallint, .integer, .bigint:
|
||||
let intKeys = try mapContent.keyVector.map { try $0.unwrap(Int.self) }
|
||||
keys = try intKeys.map { try createKey(intValue: $0) }
|
||||
default:
|
||||
let context = DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Cannot decode map with non string/integer key type"
|
||||
)
|
||||
throw DecodingError.typeMismatch(Key.self, context)
|
||||
}
|
||||
let vectors = [mapContent.valueVector]
|
||||
let mapKeys = keys.map(\.stringValue)
|
||||
let mapValues = keys.enumerated().map { KeyPath(key: $0.1, vectorIndex: 0, rowIndex: $0.0) }
|
||||
let keyMap = Dictionary(uniqueKeysWithValues: zip(mapKeys, mapValues))
|
||||
return Self(decoder: decoder, codingPath: decoder.codingPath, keyMap: keyMap, vectors: vectors)
|
||||
}
|
||||
|
||||
static func createStructContainer(
|
||||
decoder: VectorElementDataDecoder, element: Vector.Element
|
||||
) throws -> Self {
|
||||
let codingPath = decoder.codingPath
|
||||
let dataType = element.dataType
|
||||
guard element.unwrapNull() == false else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Cannot get keyed decoding container – found null value instead"
|
||||
)
|
||||
throw DecodingError.valueNotFound(Self.self, context)
|
||||
}
|
||||
guard dataType == .struct else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Expected struct column type, found \(dataType) column type instead."
|
||||
)
|
||||
throw DecodingError.typeMismatch(Self.self, context)
|
||||
}
|
||||
guard let structContents = element.structContents else {
|
||||
fatalError("Internal consistency error. Expected struct content in vector.")
|
||||
}
|
||||
let keys = try structContents.map(\.name).map(Self.createKey(stringValue:))
|
||||
let vectors = structContents.map(\.vector)
|
||||
var keyMap = [String: KeyPath]()
|
||||
for (i, key) in keys.enumerated() {
|
||||
keyMap[key.stringValue] = .init(key: key, vectorIndex: i, rowIndex: element.index)
|
||||
}
|
||||
return Self(decoder: decoder, codingPath: codingPath, keyMap: keyMap, vectors: vectors)
|
||||
}
|
||||
|
||||
private static func createKey(stringValue: String) throws -> Key {
|
||||
guard let key = Key(stringValue: stringValue) else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: [],
|
||||
debugDescription: "Cannot instatiate key of type \(Key.self) with string: \(stringValue)"
|
||||
)
|
||||
throw DecodingError.typeMismatch(Key.self, context)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
private static func createKey(intValue: Int) throws -> Key {
|
||||
guard let key = Key(intValue: intValue) else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: [],
|
||||
debugDescription: "Cannot instatiate key of type \(Key.self) with integer: \(intValue)"
|
||||
)
|
||||
throw DecodingError.typeMismatch(Key.self, context)
|
||||
}
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension VectorElementDataDecoder.UnkeyedValueContainer {
|
||||
|
||||
static func createListContainer(
|
||||
decoder: VectorElementDataDecoder, element: Vector.Element
|
||||
) throws -> Self {
|
||||
let codingPath = decoder.codingPath
|
||||
let dataType = element.dataType
|
||||
guard element.unwrapNull() == false else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Cannot get unkeyed decoding container – found null value instead"
|
||||
)
|
||||
throw DecodingError.valueNotFound(Self.self, context)
|
||||
}
|
||||
guard dataType == .list else {
|
||||
let context = DecodingError.Context(
|
||||
codingPath: codingPath,
|
||||
debugDescription: "Expected list column type, found \(dataType) column type instead."
|
||||
)
|
||||
throw DecodingError.typeMismatch(Self.self, context)
|
||||
}
|
||||
guard let childVector = element.childVector else {
|
||||
fatalError("Internal consistency error. Expected list content in vector.")
|
||||
}
|
||||
return Self(decoder: decoder, codingPath: codingPath, vector: childVector)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user