should be it
This commit is contained in:
242
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/AppenderTests.swift
vendored
Normal file
242
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/AppenderTests.swift
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
//
|
||||
// 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
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class AppenderTests: XCTestCase {
|
||||
|
||||
func test_bool_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "BOOL",
|
||||
expected: [false, true, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Bool.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_utinyint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UTINYINT",
|
||||
expected: [UInt8.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: UInt8.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_usmallint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "USMALLINT",
|
||||
expected: [UInt16.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: UInt16.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_uint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UINTEGER",
|
||||
expected: [UInt32.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: UInt32.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_ubigint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UBIGINT",
|
||||
expected: [UInt64.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: UInt64.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_tinyint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "TINYINT",
|
||||
expected: [Int8.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Int8.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_smallint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "SMALLINT",
|
||||
expected: [Int16.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Int16.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_int_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "INTEGER",
|
||||
expected: [Int32.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Int32.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_bigint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "BIGINT",
|
||||
expected: [Int64.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Int64.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_hugeint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "HUGEINT",
|
||||
expected: [IntHuge.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: IntHuge.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_uhugeint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UHUGEINT",
|
||||
expected: [UIntHuge.min, .max, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: UIntHuge.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_float_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "FLOAT",
|
||||
expected: [-Float.greatestFiniteMagnitude, .greatestFiniteMagnitude, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Float.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_double_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "DOUBLE",
|
||||
expected: [-Double.greatestFiniteMagnitude, .greatestFiniteMagnitude, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Double.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_varchar_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "VARCHAR",
|
||||
expected: ["🦆🦆🦆🦆🦆🦆", "goo\0se", nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: String.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_time_round_trip() throws {
|
||||
let t1 = Time.Components(hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Time.Components(hour: 23, minute: 59, second: 59, microsecond: 999_999)
|
||||
try roundTripTest(
|
||||
dataType: "TIME",
|
||||
expected: [Time(components: t1), Time(components: t2), nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Time.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_date_round_trip() throws {
|
||||
let d1 = Date.Components(year: -5_877_641, month: 06, day: 25)
|
||||
let d2 = Date.Components(year: 5_881_580, month: 07, day: 10)
|
||||
try roundTripTest(
|
||||
dataType: "DATE",
|
||||
expected: [Date(components: d1), Date(components: d2), nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Date.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_timestamp_round_trip() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: -290_308, month: 12, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 294_247, month: 01, day: 10, hour: 04, minute: 0, second: 54, microsecond: 775_806)
|
||||
try roundTripTest(
|
||||
dataType: "TIMESTAMP",
|
||||
expected: [Timestamp(components: t1), Timestamp(components: t2), nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Timestamp.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_interval_round_trip() throws {
|
||||
let t1 = Interval(
|
||||
years: 0, months: 0, days: 0, hours: 0, minutes: 0, seconds: 0, microseconds: 0)
|
||||
let t2 = Interval(
|
||||
years: 83, months: 3, days: 999, hours: 0, minutes: 16, seconds: 39, microseconds: 999_999)
|
||||
try roundTripTest(
|
||||
dataType: "INTERVAL",
|
||||
expected: [t1, t2, nil],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Interval.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_blob_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "BLOB",
|
||||
expected: [
|
||||
"thisisalongblob\0withnullbytes".data(using: .ascii)!,
|
||||
"\0\0\0a".data(using: .ascii)!,
|
||||
nil
|
||||
],
|
||||
append: { appender, item in try appender.append(item) },
|
||||
cast: { $0.cast(to: Data.self) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension AppenderTests {
|
||||
|
||||
func roundTripTest<T: Equatable>(
|
||||
dataType: String,
|
||||
expected: [T?],
|
||||
append: (Appender, T?) throws -> Void,
|
||||
cast: (Column<Void>) -> Column<T>
|
||||
) throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
try connection.execute("CREATE TABLE t1(i \(dataType));")
|
||||
let appender = try Appender(connection: connection, table: "t1")
|
||||
for item in expected {
|
||||
try append(appender, item)
|
||||
try appender.endRow()
|
||||
}
|
||||
try appender.flush()
|
||||
let result = try connection.query("SELECT * FROM t1;")
|
||||
let column = cast(result[0])
|
||||
for (index, item) in expected.enumerated() {
|
||||
XCTAssertEqual(column[DBInt(index)], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
47
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/CodingUserInfoKeysTests.swift
vendored
Normal file
47
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/CodingUserInfoKeysTests.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.
|
||||
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class CodingUserInfoKeysTests: XCTestCase {
|
||||
func test_logical_type() throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
try connection.execute("""
|
||||
CREATE TABLE t1(int_list INT[]);
|
||||
INSERT INTO t1 VALUES ([1, 2, 3]);
|
||||
""")
|
||||
let result = try connection.query("SELECT * FROM t1;")
|
||||
let column = result[0].cast(to: TestDynamicDecodable.self)
|
||||
XCTAssertEqual(column[0]?.logicalType?.dataType, .list)
|
||||
}
|
||||
}
|
||||
|
||||
struct TestDynamicDecodable: Decodable {
|
||||
let logicalType: LogicalType?
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
self.logicalType = decoder.userInfo[CodingUserInfoKeys.logicalTypeCodingUserInfoKey] as? LogicalType
|
||||
}
|
||||
}
|
||||
85
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/DatabaseTests.swift
vendored
Normal file
85
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/DatabaseTests.swift
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// 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 XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class DatabaseTests: XCTestCase {
|
||||
|
||||
func test_create_in_memory_database() throws {
|
||||
let _ = try Database(store: .inMemory)
|
||||
}
|
||||
|
||||
func test_create_local_file_datebase() throws {
|
||||
let fileURL = try Self.generateTemporaryFileURL(forFileNamed: "test.tb")
|
||||
defer { Self.cleanUpTemporaryFileURL(fileURL) }
|
||||
let _ = try Database(store: .file(at: fileURL))
|
||||
}
|
||||
|
||||
func test_connect_to_in_memory_datebase() throws {
|
||||
let _ = try Database(store: .inMemory).connect()
|
||||
}
|
||||
|
||||
func test_connect_to_local_file_datebase() throws {
|
||||
let fileURL = try Self.generateTemporaryFileURL(forFileNamed: "test.tb")
|
||||
defer { Self.cleanUpTemporaryFileURL(fileURL) }
|
||||
let _ = try Database(store: .file(at: fileURL)).connect()
|
||||
}
|
||||
|
||||
func test_execute_statement() throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
try connection.execute("SELECT version();")
|
||||
}
|
||||
|
||||
func test_query_result() throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
let result = try connection.query("SELECT * FROM test_all_types();")
|
||||
let element: Void? = result[0][0]
|
||||
XCTAssertNotNil(element)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Temp Directory Helpers
|
||||
|
||||
private extension DatabaseTests {
|
||||
|
||||
static func generateTemporaryFileURL(forFileNamed fileName: String) throws -> URL {
|
||||
let tmpDirURL = try FileManager.default.url(
|
||||
for: .itemReplacementDirectory,
|
||||
in: .userDomainMask,
|
||||
appropriateFor: FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0],
|
||||
create: true
|
||||
)
|
||||
return tmpDirURL.appendingPathComponent(fileName)
|
||||
}
|
||||
|
||||
static func cleanUpTemporaryFileURL(_ fileURL: URL) {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: fileURL.deletingLastPathComponent())
|
||||
}
|
||||
catch {
|
||||
print("Ignored failed attempt to remove temp dir at \(fileURL):\n\t\(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
52
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/DecimalUtilityTests.swift
vendored
Normal file
52
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/DecimalUtilityTests.swift
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// 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
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class DecimalUtilityTests: XCTestCase {
|
||||
|
||||
func test_decimal_int_huge_min() throws {
|
||||
let minInt128 = Decimal(string: "\(IntHuge.min)")!
|
||||
XCTAssertEqual(Decimal(IntHuge.min), minInt128)
|
||||
}
|
||||
|
||||
func test_decimal_int_huge_max() throws {
|
||||
let maxInt128 = Decimal(string: "\(IntHuge.max)")!
|
||||
XCTAssertEqual(Decimal(IntHuge.max), maxInt128)
|
||||
}
|
||||
|
||||
func test_decimal_uint_huge_min() throws {
|
||||
let minUInt128 = Decimal(string: "\(UIntHuge.min)")!
|
||||
XCTAssertEqual(Decimal(UIntHuge.min), minUInt128)
|
||||
}
|
||||
|
||||
func test_decimal_uint_huge_max() throws {
|
||||
let maxUInt128 = Decimal(string: "\(UIntHuge.max)")!
|
||||
XCTAssertEqual(Decimal(UIntHuge.max), maxUInt128)
|
||||
}
|
||||
}
|
||||
|
||||
60
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/ExtensionTests.swift
vendored
Normal file
60
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/ExtensionTests.swift
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// 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
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class ExtensionTests: XCTestCase {
|
||||
func test_parquet() throws {
|
||||
try loadExtension(extension_name: "parquet")
|
||||
}
|
||||
func test_json() throws {
|
||||
try loadExtension(extension_name: "json")
|
||||
}
|
||||
func test_icu() throws {
|
||||
try loadExtension(extension_name: "icu")
|
||||
}
|
||||
}
|
||||
|
||||
private extension ExtensionTests {
|
||||
func loadExtension(extension_name: String) throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
|
||||
try connection.execute("LOAD \(extension_name)")
|
||||
|
||||
let prepped = try PreparedStatement(
|
||||
connection: connection,
|
||||
query: "SELECT installed, loaded FROM duckdb_extensions() where extension_name = $1"
|
||||
)
|
||||
|
||||
try prepped.bind(extension_name, at: 1);
|
||||
|
||||
let result = try prepped.execute()
|
||||
|
||||
XCTAssertEqual(result[0].cast(to: Bool.self)[0], true)
|
||||
XCTAssertEqual(result[1].cast(to: Bool.self)[0], true)
|
||||
}
|
||||
}
|
||||
405
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/LogicalTypeTests.swift
vendored
Normal file
405
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/LogicalTypeTests.swift
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
//
|
||||
// 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
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class LogicalTypeTests: XCTestCase {
|
||||
|
||||
func test_boolean() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "BOOL",
|
||||
cast: { $0.cast(to: Bool.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .boolean)
|
||||
XCTAssertEqual($0.underlyingDataType, .boolean)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_tinyint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "TINYINT",
|
||||
cast: { $0.cast(to: Int8.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .tinyint)
|
||||
XCTAssertEqual($0.underlyingDataType, .tinyint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_smallint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "SMALLINT",
|
||||
cast: { $0.cast(to: Int16.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .smallint)
|
||||
XCTAssertEqual($0.underlyingDataType, .smallint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_integer() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "INTEGER",
|
||||
cast: { $0.cast(to: Int32.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .integer)
|
||||
XCTAssertEqual($0.underlyingDataType, .integer)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_bigint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "BIGINT",
|
||||
cast: { $0.cast(to: Int64.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .bigint)
|
||||
XCTAssertEqual($0.underlyingDataType, .bigint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_hugeint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "HUGEINT",
|
||||
cast: { $0.cast(to: IntHuge.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .hugeint)
|
||||
XCTAssertEqual($0.underlyingDataType, .hugeint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_uhugeint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "UHUGEINT",
|
||||
cast: { $0.cast(to: UIntHuge.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .uhugeint)
|
||||
XCTAssertEqual($0.underlyingDataType, .uhugeint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_utinyint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "UTINYINT",
|
||||
cast: { $0.cast(to: UInt8.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .utinyint)
|
||||
XCTAssertEqual($0.underlyingDataType, .utinyint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_usmallint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "USMALLINT",
|
||||
cast: { $0.cast(to: UInt16.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .usmallint)
|
||||
XCTAssertEqual($0.underlyingDataType, .usmallint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_uinteger() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "UINTEGER",
|
||||
cast: { $0.cast(to: UInt32.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .uinteger)
|
||||
XCTAssertEqual($0.underlyingDataType, .uinteger)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_ubigint() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "UBIGINT",
|
||||
cast: { $0.cast(to: UInt64.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .ubigint)
|
||||
XCTAssertEqual($0.underlyingDataType, .ubigint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_float() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "FLOAT",
|
||||
cast: { $0.cast(to: Float.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .float)
|
||||
XCTAssertEqual($0.underlyingDataType, .float)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_double() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "DOUBLE",
|
||||
cast: { $0.cast(to: Double.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .double)
|
||||
XCTAssertEqual($0.underlyingDataType, .double)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_timestamp() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "TIMESTAMP",
|
||||
cast: { $0.cast(to: Timestamp.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .timestamp)
|
||||
XCTAssertEqual($0.underlyingDataType, .timestamp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_timestamp_s() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "TIMESTAMP_S",
|
||||
cast: { $0.cast(to: Timestamp.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .timestampS)
|
||||
XCTAssertEqual($0.underlyingDataType, .timestampS)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_timestamp_ms() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "TIMESTAMP_MS",
|
||||
cast: { $0.cast(to: Timestamp.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .timestampMS)
|
||||
XCTAssertEqual($0.underlyingDataType, .timestampMS)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_timestamp_ns() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "TIMESTAMP_NS",
|
||||
cast: { $0.cast(to: Timestamp.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .timestampNS)
|
||||
XCTAssertEqual($0.underlyingDataType, .timestampNS)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_date() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "DATE",
|
||||
cast: { $0.cast(to: Date.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .date)
|
||||
XCTAssertEqual($0.underlyingDataType, .date)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_time() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "TIME",
|
||||
cast: { $0.cast(to: Time.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .time)
|
||||
XCTAssertEqual($0.underlyingDataType, .time)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_interval() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "INTERVAL",
|
||||
cast: { $0.cast(to: Interval.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .interval)
|
||||
XCTAssertEqual($0.underlyingDataType, .interval)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_varchar() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "VARCHAR",
|
||||
cast: { $0.cast(to: String.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .varchar)
|
||||
XCTAssertEqual($0.underlyingDataType, .varchar)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_blob() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "BLOB",
|
||||
cast: { $0.cast(to: Data.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .blob)
|
||||
XCTAssertEqual($0.underlyingDataType, .blob)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_decimal() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "DECIMAL(38, 10)",
|
||||
cast: { $0.cast(to: Decimal.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .decimal)
|
||||
XCTAssertEqual($0.underlyingDataType, .decimal)
|
||||
|
||||
let properties = $0.decimalProperties!
|
||||
XCTAssertEqual(properties.scale, 10)
|
||||
XCTAssertEqual(properties.width, 38)
|
||||
XCTAssertEqual(properties.storageType, .hugeint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
enum Mood: String, Equatable, Decodable {
|
||||
case sad
|
||||
case ok
|
||||
case happy
|
||||
}
|
||||
|
||||
func test_enum() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "mood",
|
||||
cast: { $0.cast(to: Mood.self) },
|
||||
before: { connection in
|
||||
try connection.execute("CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');")
|
||||
},
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .enum)
|
||||
XCTAssertEqual($0.underlyingDataType, .utinyint)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_list() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "INT[]",
|
||||
cast: { $0.cast(to: [Int32?].self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .list)
|
||||
XCTAssertEqual($0.underlyingDataType, .list)
|
||||
XCTAssertEqual($0.listChildType?.dataType, .integer)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
struct NumStrStruct: Equatable, Decodable {
|
||||
let num: Int32
|
||||
let str: String
|
||||
}
|
||||
|
||||
func test_struct() throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
try connection.execute("CREATE TABLE t1(num INTEGER, str VARCHAR);")
|
||||
let result = try connection.query("SELECT {'num': num, 'str': str} as struct_column FROM t1;")
|
||||
let logicalType = result[0].cast(to: NumStrStruct.self).underlyingLogicalType
|
||||
|
||||
XCTAssertEqual(logicalType.dataType, .struct)
|
||||
XCTAssertEqual(logicalType.underlyingDataType, .struct)
|
||||
|
||||
let properties = logicalType.structMemberProperties!
|
||||
XCTAssertEqual(properties.count, 2)
|
||||
XCTAssertEqual(properties[0].name, "num")
|
||||
XCTAssertEqual(properties[0].type.dataType, .integer)
|
||||
XCTAssertEqual(properties[1].name, "str")
|
||||
XCTAssertEqual(properties[1].type.dataType, .varchar)
|
||||
}
|
||||
|
||||
func test_map() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "MAP(INT,DOUBLE)",
|
||||
cast: { $0.cast(to: [Int32: Double].self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .map)
|
||||
XCTAssertEqual($0.underlyingDataType, .map)
|
||||
XCTAssertEqual($0.mapKeyType?.dataType, .integer)
|
||||
XCTAssertEqual($0.mapValueType?.dataType, .double)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
enum NumStrUnion: Equatable, Decodable {
|
||||
case num(Int32)
|
||||
case str(String)
|
||||
}
|
||||
|
||||
func test_union() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "UNION(num INT, str VARCHAR)",
|
||||
cast: { $0.cast(to: NumStrUnion.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .union)
|
||||
XCTAssertEqual($0.underlyingDataType, .union)
|
||||
|
||||
let properties = $0.unionMemberProperties!
|
||||
XCTAssertEqual(properties.count, 2)
|
||||
XCTAssertEqual(properties[0].name, "num")
|
||||
XCTAssertEqual(properties[0].type.dataType, .integer)
|
||||
XCTAssertEqual(properties[1].name, "str")
|
||||
XCTAssertEqual(properties[1].type.dataType, .varchar)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func test_uuid() throws {
|
||||
try logicalTypeTest(
|
||||
dataType: "UUID",
|
||||
cast: { $0.cast(to: UUID.self) },
|
||||
validate: {
|
||||
XCTAssertEqual($0.dataType, .uuid)
|
||||
XCTAssertEqual($0.underlyingDataType, .uuid)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension LogicalTypeTests {
|
||||
|
||||
func logicalTypeTest<T: Equatable>(
|
||||
dataType: String,
|
||||
cast: (Column<Void>) -> Column<T>,
|
||||
before: ((Connection) throws -> Void)? = nil,
|
||||
validate: (LogicalType) -> Void
|
||||
) throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
try before?(connection)
|
||||
try connection.execute("CREATE TABLE t1(i \(dataType));")
|
||||
let result = try connection.query("SELECT * FROM t1;")
|
||||
validate(cast(result[0]).underlyingLogicalType)
|
||||
}
|
||||
}
|
||||
294
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/PreparedStatementTests.swift
vendored
Normal file
294
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/PreparedStatementTests.swift
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
//
|
||||
// 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
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class PreparedStatementTests: XCTestCase {
|
||||
|
||||
func test_bool_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "BOOL",
|
||||
expected: [false, true, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Bool.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_utinyint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UTINYINT",
|
||||
expected: [UInt8.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: UInt8.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_usmallint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "USMALLINT",
|
||||
expected: [UInt16.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: UInt16.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_uint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UINTEGER",
|
||||
expected: [UInt32.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: UInt32.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_ubigint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UBIGINT",
|
||||
expected: [UInt64.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: UInt64.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_tinyint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "TINYINT",
|
||||
expected: [Int8.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Int8.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_smallint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "SMALLINT",
|
||||
expected: [Int16.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Int16.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_int_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "INTEGER",
|
||||
expected: [Int32.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Int32.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_bigint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "BIGINT",
|
||||
expected: [Int64.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Int64.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_hugeint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "HUGEINT",
|
||||
expected: [IntHuge.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: IntHuge.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_uhugeint_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "UHUGEINT",
|
||||
expected: [UIntHuge.min, .max, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: UIntHuge.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_float_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "FLOAT",
|
||||
expected: [-Float.greatestFiniteMagnitude, .greatestFiniteMagnitude, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Float.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_double_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "DOUBLE",
|
||||
expected: [-Double.greatestFiniteMagnitude, .greatestFiniteMagnitude, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Double.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_varchar_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "VARCHAR",
|
||||
expected: ["🦆🦆🦆🦆🦆🦆", "goo\0se", nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: String.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_time_round_trip() throws {
|
||||
let t1 = Time.Components(hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Time.Components(hour: 23, minute: 59, second: 59, microsecond: 999_999)
|
||||
try roundTripTest(
|
||||
dataType: "TIME",
|
||||
expected: [Time(components: t1), Time(components: t2), nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Time.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_date_round_trip() throws {
|
||||
let d1 = Date.Components(year: -5_877_641, month: 06, day: 25)
|
||||
let d2 = Date.Components(year: 5_881_580, month: 07, day: 10)
|
||||
try roundTripTest(
|
||||
dataType: "DATE",
|
||||
expected: [Date(components: d1), Date(components: d2), nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Date.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_timestamp_round_trip() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: -290_308, month: 12, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 294_247, month: 01, day: 10, hour: 04, minute: 0, second: 54, microsecond: 775_806)
|
||||
try roundTripTest(
|
||||
dataType: "TIMESTAMP",
|
||||
expected: [Timestamp(components: t1), Timestamp(components: t2), nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Timestamp.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_interval_round_trip() throws {
|
||||
let t1 = Interval(
|
||||
years: 0, months: 0, days: 0, hours: 0, minutes: 0, seconds: 0, microseconds: 0)
|
||||
let t2 = Interval(
|
||||
years: 83, months: 3, days: 999, hours: 0, minutes: 16, seconds: 39, microseconds: 999_999)
|
||||
try roundTripTest(
|
||||
dataType: "INTERVAL",
|
||||
expected: [t1, t2, nil],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Interval.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_blob_round_trip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "BLOB",
|
||||
expected: [
|
||||
"thisisalongblob\0withnullbytes".data(using: .ascii)!,
|
||||
"\0\0\0a".data(using: .ascii)!,
|
||||
nil
|
||||
],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Data.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_decimal_4_1_roundtrip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "DECIMAL(4,1)",
|
||||
expected: [
|
||||
Decimal(string: "-999.9"),
|
||||
Decimal(string: " 999.9"),
|
||||
nil
|
||||
],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Decimal.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_decimal_9_4_roundtrip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "DECIMAL(9,4)",
|
||||
expected: [
|
||||
Decimal(string: "-99999.9999"),
|
||||
Decimal(string: " 99999.9999"),
|
||||
nil
|
||||
],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Decimal.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_decimal_18_6_roundtrip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "DECIMAL(18,6)",
|
||||
expected: [
|
||||
Decimal(string: "-999999999999.999999"),
|
||||
Decimal(string: "999999999999.999999"),
|
||||
nil
|
||||
],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Decimal.self) }
|
||||
)
|
||||
}
|
||||
|
||||
func test_decimal_38_10_roundtrip() throws {
|
||||
try roundTripTest(
|
||||
dataType: "DECIMAL(38,10)",
|
||||
expected: [
|
||||
Decimal(string: "-9999999999999999999999999999.9999999999"),
|
||||
Decimal(string: "9999999999999999999999999999.9999999999"),
|
||||
nil
|
||||
],
|
||||
bind: { statement, item in try statement.bind(item, at: 1) },
|
||||
cast: { $0.cast(to: Decimal.self) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PreparedStatementTests {
|
||||
|
||||
func roundTripTest<T: Equatable>(
|
||||
dataType: String,
|
||||
expected: [T?],
|
||||
bind: (PreparedStatement, T?) throws -> Void,
|
||||
cast: (Column<Void>) -> Column<T>
|
||||
) throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
try connection.execute("CREATE TABLE t1(i \(dataType));")
|
||||
let statement = try PreparedStatement(
|
||||
connection: connection, query: "INSERT INTO t1 VALUES ($1);")
|
||||
for item in expected {
|
||||
try bind(statement, item)
|
||||
let _ = try statement.execute()
|
||||
}
|
||||
let result = try connection.query("SELECT * FROM t1;")
|
||||
let column = cast(result[0])
|
||||
for (index, item) in expected.enumerated() {
|
||||
XCTAssertEqual(column[DBInt(index)], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
440
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/TypeConversionTests.swift
vendored
Normal file
440
external/duckdb/tools/swift/duckdb-swift/Tests/DuckDBTests/TypeConversionTests.swift
vendored
Normal file
@@ -0,0 +1,440 @@
|
||||
//
|
||||
// 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
|
||||
import XCTest
|
||||
@testable import DuckDB
|
||||
|
||||
final class TypeConversionTests: XCTestCase {
|
||||
|
||||
func test_extract_from_bool() throws {
|
||||
try extractTest(
|
||||
testColumnName: "bool", expected: [false, true, nil]) { $0.cast(to: Bool.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_utinyint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "utinyint", expected: [UInt8.min, .max, nil]) { $0.cast(to: UInt8.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_usmallint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "usmallint", expected: [UInt16.min, .max, nil]) { $0.cast(to: UInt16.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_uint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "uint", expected: [UInt32.min, .max, nil]) { $0.cast(to: UInt32.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_ubigint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "ubigint", expected: [UInt64.min, .max, nil]) { $0.cast(to: UInt64.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_tinyint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "tinyint", expected: [Int8.min, .max, nil]) { $0.cast(to: Int8.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_smallint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "smallint", expected: [Int16.min, .max, nil]) { $0.cast(to: Int16.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_int() throws {
|
||||
try extractTest(
|
||||
testColumnName: "int", expected: [Int32.min, .max, nil]) { $0.cast(to: Int32.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_bigint() throws {
|
||||
try extractTest(
|
||||
testColumnName: "bigint", expected: [Int64.min, .max, nil]) { $0.cast(to: Int64.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_hugeint() throws {
|
||||
let expected = [IntHuge.min, IntHuge.max, nil]
|
||||
try extractTest(testColumnName: "hugeint", expected: expected) { $0.cast(to: IntHuge.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_uhugeint() throws {
|
||||
let expected = [UIntHuge.min, UIntHuge.max, nil]
|
||||
try extractTest(testColumnName: "uhugeint", expected: expected) { $0.cast(to: UIntHuge.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_float() throws {
|
||||
let expected = [-Float.greatestFiniteMagnitude, .greatestFiniteMagnitude, nil]
|
||||
try extractTest(testColumnName: "float", expected: expected) { $0.cast(to: Float.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_double() throws {
|
||||
let expected = [-Double.greatestFiniteMagnitude, .greatestFiniteMagnitude, nil]
|
||||
try extractTest(testColumnName: "double", expected: expected) { $0.cast(to: Double.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_varchar() throws {
|
||||
let expected = ["🦆🦆🦆🦆🦆🦆", "goo\0se", nil]
|
||||
try extractTest(testColumnName: "varchar", expected: expected) { $0.cast(to: String.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_uuid() throws {
|
||||
let expected = [
|
||||
UUID(uuidString: "00000000-0000-0000-0000-000000000000"),
|
||||
UUID(uuidString: "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "uuid", expected: expected) { $0.cast(to: UUID.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_time() throws {
|
||||
let expected = [
|
||||
Time(components: .init(hour: 0, minute: 0, second: 0, microsecond: 0)),
|
||||
Time(components: .init(hour: 24, minute: 0, second: 0, microsecond: 0)),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "time", expected: expected) { $0.cast(to: Time.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_time_tz() throws {
|
||||
let expected = [
|
||||
TimeTz(time: Time(components: .init(hour: 0, minute: 0, second: 0, microsecond: 0)), offset: 57599),
|
||||
TimeTz(time: Time(components: .init(hour: 24, minute: 0, second: 0, microsecond: 0)), offset: -57599),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "time_tz", expected: expected) { $0.cast(to: TimeTz.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_date() throws {
|
||||
let expected = [
|
||||
Date(components: .init(year: -5_877_641, month: 06, day: 25)),
|
||||
Date(components: .init(year: 5_881_580, month: 07, day: 10)),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "date", expected: expected) { $0.cast(to: Date.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_timestamp() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: -290_308, month: 12, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 294_247, month: 01, day: 10, hour: 04, minute: 0, second: 54, microsecond: 775_806)
|
||||
let expected = [Timestamp(components: t1), Timestamp(components: t2), nil]
|
||||
try extractTest(testColumnName: "timestamp", expected: expected) { $0.cast(to: Timestamp.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_timestamp_tz() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: -290_308, month: 12, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 294_247, month: 01, day: 10, hour: 04, minute: 0, second: 54, microsecond: 775_806)
|
||||
let expected = [Timestamp(components: t1), Timestamp(components: t2), nil]
|
||||
try extractTest(
|
||||
testColumnName: "timestamp_tz", expected: expected) { $0.cast(to: Timestamp.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_timestamp_s() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: -290_308, month: 12, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 294_247, month: 01, day: 10, hour: 04, minute: 0, second: 54, microsecond: 0)
|
||||
let expected = [Timestamp(components: t1), Timestamp(components: t2), nil]
|
||||
try extractTest(
|
||||
testColumnName: "timestamp_s", expected: expected) { $0.cast(to: Timestamp.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_timestamp_ms() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: -290_308, month: 12, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 294_247, month: 01, day: 10, hour: 04, minute: 0, second: 54, microsecond: 775_000)
|
||||
let expected = [Timestamp(components: t1), Timestamp(components: t2), nil]
|
||||
try extractTest(
|
||||
testColumnName: "timestamp_ms", expected: expected) { $0.cast(to: Timestamp.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_timestamp_ns() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: 1677, month: 09, day: 22, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 2262, month: 04, day: 11, hour: 23, minute: 47, second: 16, microsecond: 854_775)
|
||||
let expected = [Timestamp(components: t1), Timestamp(components: t2), nil]
|
||||
try extractTest(
|
||||
testColumnName: "timestamp_ns", expected: expected) { $0.cast(to: Timestamp.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_interval() throws {
|
||||
let expected = [
|
||||
Interval(
|
||||
years: 0, months: 0, days: 0, hours: 0, minutes: 0, seconds: 0, microseconds: 0),
|
||||
Interval(
|
||||
years: 83, months: 3, days: 999, hours: 0, minutes: 16, seconds: 39, microseconds: 999_999),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "interval", expected: expected) { $0.cast(to: Interval.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_blob() throws {
|
||||
let expected = [
|
||||
"thisisalongblob\0withnullbytes".data(using: .ascii)!,
|
||||
"\0\0\0a".data(using: .ascii)!,
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "blob", expected: expected) { $0.cast(to: Data.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_decimal_4_1() throws {
|
||||
let expected = [
|
||||
Decimal(string: "-999.9"),
|
||||
Decimal(string: " 999.9"),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "dec_4_1", expected: expected) { $0.cast(to: Decimal.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_decimal_9_4() throws {
|
||||
let expected = [
|
||||
Decimal(string: "-99999.9999"),
|
||||
Decimal(string: " 99999.9999"),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "dec_9_4", expected: expected) { $0.cast(to: Decimal.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_decimal_18_6() throws {
|
||||
let expected = [
|
||||
Decimal(string: "-999999999999.999999"),
|
||||
Decimal(string: "999999999999.999999"),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "dec_18_6", expected: expected) { $0.cast(to: Decimal.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_decimal_38_10() throws {
|
||||
let expected = [
|
||||
Decimal(string: "-9999999999999999999999999999.9999999999"),
|
||||
Decimal(string: "9999999999999999999999999999.9999999999"),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "dec38_10", expected: expected) { $0.cast(to: Decimal.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_enum_small() throws {
|
||||
enum SmallEnum: UInt8, RawRepresentable, Decodable {
|
||||
case duckDuckEnum
|
||||
case goose
|
||||
}
|
||||
let expected = [SmallEnum.duckDuckEnum, .goose, nil]
|
||||
try extractTest(
|
||||
testColumnName: "small_enum",
|
||||
expected: expected,
|
||||
params: "use_large_enum=true"
|
||||
) { $0.cast(to: SmallEnum.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_enum_medium() throws {
|
||||
enum MediumEnum: UInt16, RawRepresentable, Decodable {
|
||||
case enum0
|
||||
case enum299 = 299
|
||||
}
|
||||
let expected = [MediumEnum.enum0, .enum299, nil]
|
||||
try extractTest(
|
||||
testColumnName: "medium_enum",
|
||||
expected: expected,
|
||||
params: "use_large_enum=true"
|
||||
) { $0.cast(to: MediumEnum.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_enum_large() throws {
|
||||
enum LargeEnum: UInt32, RawRepresentable, Decodable {
|
||||
case enum0
|
||||
case enum69_999 = 69_999
|
||||
}
|
||||
let expected = [LargeEnum.enum0, .enum69_999, nil]
|
||||
try extractTest(
|
||||
testColumnName: "large_enum",
|
||||
expected: expected,
|
||||
params: "use_large_enum=true"
|
||||
) { $0.cast(to: LargeEnum.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_int_array() throws {
|
||||
let expected = [[], [Int32(42), 999, nil, nil, -42], nil]
|
||||
try extractTest(testColumnName: "int_array", expected: expected) { $0.cast(to: [Int32?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_int_array_casting_to_swift_int() throws {
|
||||
let expected = [[], [Int(42), 999, nil, nil, -42], nil]
|
||||
try extractTest(testColumnName: "int_array", expected: expected) { $0.cast(to: [Int?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_double_array() throws {
|
||||
// We need this contraption to work around .nan != .nan
|
||||
enum DoubleBox: Equatable {
|
||||
case normal(Double?)
|
||||
case nan
|
||||
init(_ source: Double?) {
|
||||
switch source {
|
||||
case let source? where source.isNaN:
|
||||
self = .nan
|
||||
case let source:
|
||||
self = .normal(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
let source = [[], [Double(42), .nan, .infinity, -.infinity, nil, -42], nil]
|
||||
let expected = source.map { $0?.map(DoubleBox.init(_:)) }
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
let result = try connection.query("SELECT double_array FROM test_all_types();")
|
||||
let column = result[0].cast(to: [Double?].self)
|
||||
for (index, item) in expected.enumerated() {
|
||||
XCTAssertEqual(column[DBInt(index)]?.map(DoubleBox.init(_:)), item)
|
||||
}
|
||||
}
|
||||
|
||||
func test_extract_from_date_array() throws {
|
||||
let expected = [
|
||||
[],
|
||||
[
|
||||
Date(components: .init(year: 1970, month: 01, day: 01)),
|
||||
Date(days: 2147483647),
|
||||
Date(days: -2147483647),
|
||||
nil,
|
||||
Date(components: .init(year: 2022, month: 05, day: 12)),
|
||||
],
|
||||
nil
|
||||
]
|
||||
try extractTest(
|
||||
testColumnName: "date_array", expected: expected
|
||||
) { $0.cast(to: [DuckDB.Date?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_timestamptz_array() throws {
|
||||
let t1 = Timestamp.Components(
|
||||
year: 1970, month: 01, day: 01, hour: 0, minute: 0, second: 0, microsecond: 0)
|
||||
let t2 = Timestamp.Components(
|
||||
year: 2022, month: 05, day: 12, hour: 23, minute: 23, second: 45, microsecond: 0)
|
||||
let expected = [
|
||||
[],
|
||||
[
|
||||
Timestamp(components: t1),
|
||||
Timestamp(microseconds: 9223372036854775807),
|
||||
Timestamp(microseconds: -9223372036854775807),
|
||||
nil,
|
||||
Timestamp(components: t2),
|
||||
],
|
||||
nil
|
||||
]
|
||||
try extractTest(
|
||||
testColumnName: "timestamptz_array", expected: expected
|
||||
) { $0.cast(to: [Timestamp?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_varchar_array() throws {
|
||||
let expected = [[], ["🦆🦆🦆🦆🦆🦆", "goose", nil, ""], nil]
|
||||
try extractTest(
|
||||
testColumnName: "varchar_array", expected: expected
|
||||
) { $0.cast(to: [String?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_nested_int_array() throws {
|
||||
let expected = [
|
||||
[],
|
||||
[[], [Int32(42), 999, nil, nil, -42], nil, [], [42, 999, nil, nil, -42]],
|
||||
nil
|
||||
]
|
||||
try extractTest(
|
||||
testColumnName: "nested_int_array", expected: expected
|
||||
) { $0.cast(to: [[Int32?]?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_struct() throws {
|
||||
struct TestStruct: Decodable, Equatable {
|
||||
var a: Int32? = nil
|
||||
var b: String? = nil
|
||||
}
|
||||
let expected = [
|
||||
TestStruct(),
|
||||
TestStruct(a: 42, b: "🦆🦆🦆🦆🦆🦆"),
|
||||
nil
|
||||
]
|
||||
try extractTest(testColumnName: "struct", expected: expected) { $0.cast(to: TestStruct.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_struct_of_arrays() throws {
|
||||
struct TestStruct: Decodable, Equatable {
|
||||
var a: [Int32?]? = nil
|
||||
var b: [String?]? = nil
|
||||
}
|
||||
let expected = [
|
||||
TestStruct(a: nil, b: nil),
|
||||
TestStruct(a: [42, 999, nil, nil, -42], b: ["🦆🦆🦆🦆🦆🦆", "goose", nil, ""]),
|
||||
nil
|
||||
]
|
||||
try extractTest(
|
||||
testColumnName: "struct_of_arrays", expected: expected) { $0.cast(to: TestStruct.self) }
|
||||
}
|
||||
|
||||
func test_extract_from_array_of_structs() throws {
|
||||
struct TestStruct: Decodable, Equatable {
|
||||
var a: Int32? = nil
|
||||
var b: String? = nil
|
||||
}
|
||||
let expected = [
|
||||
[],
|
||||
[TestStruct(a: nil, b: nil), TestStruct(a: 42, b: "🦆🦆🦆🦆🦆🦆"), nil],
|
||||
nil
|
||||
]
|
||||
try extractTest(
|
||||
testColumnName: "array_of_structs", expected: expected) { $0.cast(to: [TestStruct?].self) }
|
||||
}
|
||||
|
||||
func test_extract_from_map() throws {
|
||||
let expected = [
|
||||
Dictionary(),
|
||||
Dictionary(uniqueKeysWithValues: [("key1", "🦆🦆🦆🦆🦆🦆"), ("key2", "goose")]),
|
||||
nil
|
||||
]
|
||||
try extractTest(
|
||||
testColumnName: "map", expected: expected) { $0.cast(to: [String: String].self) }
|
||||
}
|
||||
}
|
||||
|
||||
private extension TypeConversionTests {
|
||||
|
||||
func extractTest<T: Equatable>(
|
||||
testColumnName: String,
|
||||
expected: [T?],
|
||||
params: String = "",
|
||||
cast: (Column<Void>) -> Column<T>
|
||||
) throws {
|
||||
let connection = try Database(store: .inMemory).connect()
|
||||
let result = try connection.query("SELECT \(testColumnName) FROM test_all_types(\(params));")
|
||||
let column = cast(result[0])
|
||||
for (index, item) in expected.enumerated() {
|
||||
XCTAssertEqual(column[DBInt(index)], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user