should be it
This commit is contained in:
432
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Appender.swift
vendored
Normal file
432
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Appender.swift
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// An object that efficiently appends data to a DuckDB table
|
||||
///
|
||||
/// Appenders are the most efficient way of loading data into DuckDB from within
|
||||
/// Swift, and are recommended for fast data loading. The appender is much
|
||||
/// faster than using prepared statements or individual INSERT INTO statements.
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column an `append(_:)` call
|
||||
/// should be made after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// The following example shows two rows being appended to a table with a two
|
||||
/// column layout.
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// let database = try Database(store: .inMemory)
|
||||
/// let connection = try database.connect()
|
||||
/// try connection.execute("CREATE TABLE people(id INTEGER, name VARCHAR)")
|
||||
/// let appender = try Appender(connection: connection, table: "people")
|
||||
/// // add first row
|
||||
/// try appender.append(Int32(1))
|
||||
/// try appender.append("Mark")
|
||||
/// try appender.endRow()
|
||||
/// // add second row
|
||||
/// try appender.append(Int32(2))
|
||||
/// try appender.append("Hannes")
|
||||
/// try appender.endRow()
|
||||
/// // flush rows to table
|
||||
/// try appender.flush()
|
||||
/// }
|
||||
/// catch {
|
||||
/// // handle error
|
||||
/// }
|
||||
/// ```
|
||||
public final class Appender {
|
||||
|
||||
private let connection: Connection
|
||||
private let ptr = UnsafeMutablePointer<duckdb_appender?>.allocate(capacity: 1)
|
||||
|
||||
/// Creates a new appender
|
||||
///
|
||||
/// Instantiates an ``Appender`` through which table rows can be efficiently
|
||||
/// added
|
||||
///
|
||||
/// - Parameter connection: the connection through which rows should be
|
||||
/// appended
|
||||
/// - Parameter schema: the database schema (defaults to `main`)
|
||||
/// - Parameter table: the table to append to
|
||||
/// - Throws: ``DatabaseError/appenderFailedToInitialize(reason:)`` if the
|
||||
/// appender failed to instantiate
|
||||
public init(connection: Connection, schema: String? = nil, table: String) throws {
|
||||
self.connection = connection
|
||||
let status = schema.withOptionalCString { schemaStrPtr in
|
||||
table.withCString { tableStrPtr in
|
||||
connection.withCConnection { duckdb_appender_create($0, schemaStrPtr, tableStrPtr, ptr) }
|
||||
}
|
||||
}
|
||||
guard .success == status else {
|
||||
throw DatabaseError.appenderFailedToInitialize(reason: appenderError())
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
try? flush()
|
||||
duckdb_appender_destroy(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
/// Signals the end of the current row
|
||||
///
|
||||
/// After all columns for a row have been appended, ``endRow()`` must be
|
||||
/// called to indicate that the row is ready to be added to the database.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToEndRow(reason:)``
|
||||
/// if the row could not be completed in its current state
|
||||
public func endRow() throws {
|
||||
let status = duckdb_appender_end_row(ptr.pointee)
|
||||
guard .success == status else {
|
||||
throw DatabaseError.appenderFailedToEndRow(reason: appenderError())
|
||||
}
|
||||
}
|
||||
|
||||
/// Flushes pending rows to the database
|
||||
///
|
||||
/// To optimize performance, the appender writes rows to the database in
|
||||
/// batches. Use `flush()` to immediately write any rows that are pending
|
||||
/// insertion.
|
||||
///
|
||||
/// - Throws: ``DatabaseError/appenderFailedToFlush(reason:)`` if the pending
|
||||
/// rows failed to be written to the database
|
||||
public func flush() throws {
|
||||
let status = duckdb_appender_flush(ptr.pointee)
|
||||
guard .success == status else {
|
||||
throw DatabaseError.appenderFailedToFlush(reason: appenderError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Appender {
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Bool?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_bool(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Int8?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_int8(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Int16?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_int16(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Int32?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_int32(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Int64?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_int64(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: IntHuge?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_hugeint(ptr.pointee, .init(value)) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: UIntHuge?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_uhugeint(ptr.pointee, .init(value)) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: UInt8?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_uint8(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: UInt16?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_uint16(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: UInt32?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_uint32(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: UInt64?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_uint64(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Float?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_float(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Double?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_double(ptr.pointee, value) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Date?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_date(ptr.pointee, .init(date: value)) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Time?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand { duckdb_append_time(ptr.pointee, .init(time: value)) }
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Timestamp?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand {
|
||||
duckdb_append_timestamp(ptr.pointee, .init(timestamp: value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Interval?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand {
|
||||
duckdb_append_interval(ptr.pointee, .init(interval: value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: String?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
let data = value.data(using: .utf8)!
|
||||
try withThrowingCommand {
|
||||
data.withUnsafeBytes { dataPtr in
|
||||
duckdb_append_varchar_length(ptr.pointee, dataPtr.baseAddress, .init(dataPtr.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a value for the current row of the given type
|
||||
///
|
||||
/// Appends are made in row-wise format. For every column, an `append(_:)`
|
||||
/// call should be made, after which the row should be finished by calling
|
||||
/// ``endRow()``.
|
||||
///
|
||||
/// - Parameter value: the value to append
|
||||
/// - Throws: ``DatabaseError/appenderFailedToAppendItem(reason:)``
|
||||
/// if a value of this type was not expected in the appender's current state
|
||||
func append(_ value: Data?) throws {
|
||||
guard let value = try unwrapValueOrAppendNull(value) else { return }
|
||||
try withThrowingCommand {
|
||||
value.withUnsafeBytes { dataPtr in
|
||||
duckdb_append_blob( ptr.pointee, dataPtr.baseAddress, .init(dataPtr.count)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Appender {
|
||||
|
||||
func appendNullValue() throws {
|
||||
try withThrowingCommand { duckdb_append_null(ptr.pointee) }
|
||||
}
|
||||
|
||||
func unwrapValueOrAppendNull<T>(_ value: T?) throws -> T? {
|
||||
guard let value else {
|
||||
try appendNullValue()
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func withThrowingCommand(_ body: () throws -> duckdb_state) throws {
|
||||
let state = try body()
|
||||
guard state == .success else {
|
||||
throw DatabaseError.appenderFailedToAppendItem(reason: appenderError())
|
||||
}
|
||||
}
|
||||
|
||||
func appenderError() -> String? {
|
||||
duckdb_appender_error(ptr.pointee).map(String.init(cString:))
|
||||
}
|
||||
}
|
||||
52
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/CodingUserInfoKeys.swift
vendored
Normal file
52
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/CodingUserInfoKeys.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.
|
||||
|
||||
public struct CodingUserInfoKeys {
|
||||
/// This key is set on the `userInfo` dictionary of the `Decoder` that is
|
||||
/// used when transforming data into a `Decodable`. The value is the ``LogicalType``
|
||||
/// of the element being decoded. This can be used to implement dynamic decoding
|
||||
/// behavior based on the underlying database type.
|
||||
///
|
||||
/// For example:
|
||||
/// ```swift
|
||||
/// struct DynamicDecodable: Decodable {
|
||||
/// init(from decoder: Decoder) throws {
|
||||
/// guard let logicalType = decoder.userInfo[CodingUserInfoKeys.logicalType] as? LogicalType else {
|
||||
/// throw Error.expectedLogicalType
|
||||
/// }
|
||||
/// switch logicalType.dataType {
|
||||
/// case .list:
|
||||
/// let unkeyedContainer = try decoder.unkeyedContainer()
|
||||
/// ...
|
||||
/// case .map, .struct:
|
||||
/// let keyedContainer = try decoder.container(keyedBy: AnyCodingKey.self)
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let column = result[0].cast(to: DynamicDecodable.self)
|
||||
/// ```
|
||||
public static let logicalTypeCodingUserInfoKey = CodingUserInfoKey(rawValue: "logicalType")!
|
||||
}
|
||||
466
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Column.swift
vendored
Normal file
466
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Column.swift
vendored
Normal file
@@ -0,0 +1,466 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// A DuckDB result set column
|
||||
///
|
||||
/// DuckDB columns represent a vertical slice of a result set table. All DuckDB
|
||||
/// columns have an underlying database type (accessed via the
|
||||
/// ``underlyingDatabaseType`` member) which determine the native Swift types to
|
||||
/// which the column can be cast to.
|
||||
///
|
||||
/// When columns are initially retrieved from a ``ResultSet`` through its
|
||||
/// ``ResultSet/subscript(_:)`` accessor they have an element type of `Void`.
|
||||
/// Only after a column is cast to a matching native type can its elements be
|
||||
/// accessed.
|
||||
///
|
||||
/// For example, a column with an underlying database type of
|
||||
/// ``DatabaseType/varchar`` can be cast to type `String`:
|
||||
///
|
||||
/// ```swift
|
||||
/// // casts the first column in a result set to string
|
||||
/// let column = result[0].cast(to: String.self)
|
||||
/// ```
|
||||
///
|
||||
/// The documentation for each ``DatabaseType`` member specifies which native
|
||||
/// Swift types a column may be cast to.
|
||||
///
|
||||
/// As a column is a Swift `Collection` type, once a column has been
|
||||
/// successfully cast its elements can be accessed in the same way as any other
|
||||
/// Swift collection type.
|
||||
///
|
||||
/// ```swift
|
||||
/// for element in column {
|
||||
/// print("element: \(element)")
|
||||
/// }
|
||||
/// ```
|
||||
public struct Column<DataType> {
|
||||
|
||||
private let result: ResultSet
|
||||
private let columnIndex: DBInt
|
||||
private let unwrap: @Sendable (Vector.Element) throws -> DataType?
|
||||
|
||||
init(
|
||||
result: ResultSet,
|
||||
columnIndex: DBInt,
|
||||
unwrap: @escaping @Sendable (Vector.Element) throws -> DataType?
|
||||
) {
|
||||
self.result = result
|
||||
self.columnIndex = columnIndex
|
||||
self.unwrap = unwrap
|
||||
}
|
||||
|
||||
/// The name of the table column
|
||||
public var name: String {
|
||||
result.columnName(at: columnIndex)
|
||||
}
|
||||
|
||||
/// The native Swift type to which the column has been cast
|
||||
public var dataType: DataType.Type {
|
||||
DataType.self
|
||||
}
|
||||
|
||||
/// The underlying primitive database type of the column
|
||||
public var underlyingDatabaseType: DatabaseType {
|
||||
result.columnDataType(at: columnIndex)
|
||||
}
|
||||
|
||||
/// The underlying logical type of the column
|
||||
public var underlyingLogicalType: LogicalType {
|
||||
result.columnLogicalType(at: columnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Type Casting
|
||||
|
||||
public extension Column {
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Void.Type) -> Column<Void> {
|
||||
.init(result: result, columnIndex: columnIndex) { $0.unwrapNull() ? nil : () }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Bool.Type) -> Column<Bool> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Bool.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Warning: Implicit conversion of a DuckDB integer column value greater
|
||||
/// than `Int.max` or less than `Int.min` is a programmer error and will
|
||||
/// result in a runtime precondition failure
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Int.Type) -> Column<Int> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Int.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Int8.Type) -> Column<Int8> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Int8.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Int16.Type) -> Column<Int16> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Int16.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Int32.Type) -> Column<Int32> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Int32.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Int64.Type) -> Column<Int64> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Int64.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: IntHuge.Type) -> Column<IntHuge> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(IntHuge.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UIntHuge.Type) -> Column<UIntHuge> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UIntHuge.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Warning: Implicit conversion of a DuckDB integer column value greater
|
||||
/// than `UInt.max` is a programmer error and will result in a runtime
|
||||
/// precondition failure
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UInt.Type) -> Column<UInt> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UInt.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UInt8.Type) -> Column<UInt8> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UInt8.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UInt16.Type) -> Column<UInt16> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UInt16.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UInt32.Type) -> Column<UInt32> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UInt32.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UInt64.Type) -> Column<UInt64> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UInt64.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Float.Type) -> Column<Float> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Float.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Double.Type) -> Column<Double> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Double.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: String.Type) -> Column<String> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(String.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: UUID.Type) -> Column<UUID> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(UUID.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Time.Type) -> Column<Time> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Time.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between the
|
||||
/// given type and the column's underlying database type, returned elements
|
||||
/// will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: TimeTz.Type) -> Column<TimeTz> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(TimeTz.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Date.Type) -> Column<Date> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Date.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Timestamp.Type) -> Column<Timestamp> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Timestamp.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Interval.Type) -> Column<Interval> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Interval.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Data.Type) -> Column<Data> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Data.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast(to type: Decimal.Type) -> Column<Decimal> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrap(Decimal.self) }
|
||||
}
|
||||
|
||||
/// Casts the column to the given type
|
||||
///
|
||||
/// A column cast always succeeds but if there is a type-mismatch between
|
||||
/// the given type and the column's underlying database type, returned
|
||||
/// elements will always be equal to `nil`.
|
||||
///
|
||||
/// - Parameter type: the native Swift type to cast to
|
||||
/// - Returns: a typed DuckDB result set ``Column``
|
||||
func cast<T: Decodable>(to type: T.Type) -> Column<T> {
|
||||
.init(result: result, columnIndex: columnIndex) { try $0.unwrapDecodable(T.self) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Collection conformance
|
||||
|
||||
extension Column: RandomAccessCollection {
|
||||
|
||||
public typealias Element = DataType?
|
||||
|
||||
public struct Iterator: IteratorProtocol {
|
||||
|
||||
private let column: Column
|
||||
private var position: DBInt
|
||||
|
||||
init(column: Column) {
|
||||
self.column = column
|
||||
self.position = column.startIndex
|
||||
}
|
||||
|
||||
public mutating func next() -> Element? {
|
||||
guard position < column.endIndex else { return nil }
|
||||
defer { position += 1 }
|
||||
return .some(column[position])
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(position: DBInt) -> DataType? {
|
||||
try? unwrap(result.element(forColumn: columnIndex, at: position))
|
||||
}
|
||||
|
||||
public var startIndex: DBInt { 0 }
|
||||
public var endIndex: DBInt { result.rowCount }
|
||||
|
||||
public func makeIterator() -> Iterator {
|
||||
Iterator(column: self)
|
||||
}
|
||||
|
||||
public func index(after i: DBInt) -> DBInt { i + 1 }
|
||||
public func index(before i: DBInt) -> DBInt { i - 1 }
|
||||
}
|
||||
|
||||
// MARK: - Sendable conformance
|
||||
|
||||
extension Column: Sendable where DataType: Sendable {}
|
||||
|
||||
// MARK: - Identifiable conformance
|
||||
|
||||
extension Column: Identifiable {
|
||||
public var id: String { name }
|
||||
}
|
||||
133
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Configuration.swift
vendored
Normal file
133
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Configuration.swift
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
public extension Database {
|
||||
|
||||
/// An object representing a DuckDB database configuration
|
||||
///
|
||||
/// Configuration options can be provided to change different settings of the
|
||||
/// database system. Note that many of these settings can be changed later on
|
||||
/// using PRAGMA statements as well. The configuration object should be
|
||||
/// created, filled with values using ``setValue(_:forKey:)`` and passed to
|
||||
/// ``Database/init(store:configuration:)``
|
||||
///
|
||||
/// The following example sets up an in-memory database with some
|
||||
/// configuration options set
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// let configuration = Configuration()
|
||||
/// configuration.setValue("READ_WRITE", forKey: "access_mode")
|
||||
/// configuration.setValue("8", forKey: "threads")
|
||||
/// configuration.setValue("8GB", forKey: "max_memory")
|
||||
/// configuration.setValue("DESC", forKey: "default_order")
|
||||
/// let database = try Database(store: .inMemory, configuration: configuration)
|
||||
/// let connection = try database.connect()
|
||||
/// }
|
||||
/// catch {
|
||||
/// // handle error
|
||||
/// }
|
||||
/// ```
|
||||
/// Use ``Database/Configuration/options`` to see the full list of available
|
||||
/// configuration options.
|
||||
final class Configuration {
|
||||
|
||||
private let ptr = UnsafeMutablePointer<duckdb_config?>.allocate(capacity: 1)
|
||||
|
||||
/// Creates a DuckDB configuration
|
||||
public init() {
|
||||
let status = duckdb_create_config(ptr)
|
||||
guard status == .success else { fatalError("malloc failure") }
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_destroy_config(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
func withCConfiguration<T>(_ body: (duckdb_config?) throws -> T) rethrows -> T {
|
||||
try body(ptr.pointee)
|
||||
}
|
||||
|
||||
/// Set a value on the configuration object
|
||||
///
|
||||
/// Applies a configuration option to the configuration object. Use
|
||||
/// ``Database/Configuration/options`` to see the full list of available
|
||||
/// configuration options.
|
||||
///
|
||||
/// - Parameter key: the name of the option being set
|
||||
/// - Parameter value: the desired value of the option being set
|
||||
/// - Throws: ``DatabaseError/configurationFailedToSetFlag`` if the option
|
||||
/// is not available or the value is malformed
|
||||
public func setValue(_ value: String, forKey key: String) throws {
|
||||
try value.withCString { valueCStr in
|
||||
try key.withCString { keyCStr in
|
||||
let status = duckdb_set_config(ptr.pointee, keyCStr, valueCStr)
|
||||
guard .success == status else {
|
||||
throw DatabaseError.configurationFailedToSetFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Option
|
||||
|
||||
public extension Database.Configuration {
|
||||
|
||||
/// A DuckDB database configuration option information type
|
||||
struct OptionInfo {
|
||||
/// The name/key of the option
|
||||
public let name: String
|
||||
/// An overview of the option and its potential values
|
||||
public let description: String
|
||||
}
|
||||
|
||||
/// A list of all available database configuration options with their
|
||||
/// descriptions
|
||||
static var options: [OptionInfo] {
|
||||
let count = duckdb_config_count()
|
||||
var options = [OptionInfo]()
|
||||
let outName = UnsafeMutablePointer<UnsafePointer<CChar>?>.allocate(capacity: 1)
|
||||
let outDesc = UnsafeMutablePointer<UnsafePointer<CChar>?>.allocate(capacity: 1)
|
||||
defer {
|
||||
outName.deallocate()
|
||||
outDesc.deallocate()
|
||||
}
|
||||
for i in 0..<count {
|
||||
duckdb_get_config_flag(i, outName, outDesc)
|
||||
if let nameCStr = outName.pointee, let descCStr = outDesc.pointee {
|
||||
let name = String(cString: nameCStr)
|
||||
let desc = String(cString: descCStr)
|
||||
options.append(OptionInfo(name: name, description: desc))
|
||||
}
|
||||
outName.pointee = nil
|
||||
outDesc.pointee = nil
|
||||
}
|
||||
return options
|
||||
}
|
||||
}
|
||||
106
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Connection.swift
vendored
Normal file
106
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Connection.swift
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// An object representing a connection to a DuckDB database
|
||||
///
|
||||
/// A connection through which a database can be queried
|
||||
///
|
||||
/// For each database, you can create one or many connections using
|
||||
/// ``Database/connect()``.
|
||||
/// As individual connections are locked during querying it is recommended that
|
||||
/// in contexts where blocking is undesirable, connections should be accessed
|
||||
/// asynchronously through an actor or via a background queue.
|
||||
///
|
||||
/// The following example creates a new in-memory database and connects to it.
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// let database = try Database(store: .inMemory)
|
||||
/// let connection = try database.connect()
|
||||
/// }
|
||||
/// catch {
|
||||
/// // handle error
|
||||
/// }
|
||||
/// ```
|
||||
public final class Connection: Sendable {
|
||||
|
||||
private let database: Database
|
||||
private let ptr = UnsafeMutablePointer<duckdb_connection?>.allocate(capacity: 1)
|
||||
|
||||
/// Creates a new connection
|
||||
///
|
||||
/// Instantiates a connection through which a database can be queried
|
||||
///
|
||||
/// - Parameter database: the database to connect to
|
||||
/// - Throws: ``DatabaseError/connectionFailedToInitialize`` if the connection
|
||||
/// failed to instantiate
|
||||
public init(database: Database) throws {
|
||||
self.database = database
|
||||
let status = database.withCDatabase { duckdb_connect($0, ptr) }
|
||||
guard status == .success else { throw DatabaseError.connectionFailedToInitialize }
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_disconnect(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
/// Perform a database query
|
||||
///
|
||||
/// Takes a raw SQL statement and passes it to the database engine for
|
||||
/// execution, returning the result.
|
||||
///
|
||||
/// - Note: This is a blocking operation
|
||||
/// - Parameter sql: the query string as SQL
|
||||
/// - Throws: ``DatabaseError/connectionQueryError(reason:)`` if the query
|
||||
/// did not execute successfully
|
||||
/// - Returns: a query result set
|
||||
public func query(_ sql: String) throws -> ResultSet {
|
||||
try ResultSet(connection: self, sql: sql)
|
||||
}
|
||||
|
||||
/// Execute a database query
|
||||
///
|
||||
/// Takes a raw SQL statement and passes it to the database engine for
|
||||
/// execution, ignoring the result.
|
||||
///
|
||||
/// - Note: This is a blocking operation
|
||||
/// - Parameter sql: the query string as SQL
|
||||
/// - Throws: ``DatabaseError/connectionQueryError(reason:)`` if the query
|
||||
/// did not execute successfully
|
||||
public func execute(_ sql: String) throws {
|
||||
let status = sql.withCString { queryStrPtr in
|
||||
duckdb_query(ptr.pointee, queryStrPtr, nil)
|
||||
}
|
||||
guard status == .success else {
|
||||
throw DatabaseError.connectionQueryError(reason: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func withCConnection<T>(_ body: (duckdb_connection?) throws -> T) rethrows -> T {
|
||||
try body(ptr.pointee)
|
||||
}
|
||||
}
|
||||
135
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Database.swift
vendored
Normal file
135
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Database.swift
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// DuckDB index type
|
||||
public typealias DBInt = UInt64
|
||||
|
||||
/// An object representing a DuckDB database
|
||||
///
|
||||
/// To use DuckDB, you must first initialize a DuckDB ``Database``.
|
||||
/// ``Database/init(store:configuration:)`` takes as parameter the database
|
||||
/// store type. The ``Database/Store/inMemory`` option can be used to create an
|
||||
/// in-memory database. Note that for an in-memory database no data is persisted
|
||||
/// to disk (i.e. all data is lost when you exit the process).
|
||||
///
|
||||
/// With the ``Database`` instantiated, you can create one or many DuckDB
|
||||
/// ``Connection`` instances using ``Database/connect()``. As individual
|
||||
/// connections are locked during querying it is recommended that in contexts
|
||||
/// where blocking is undesirable, connections should be accessed
|
||||
/// asynchronously through an actor or via a background queue.
|
||||
///
|
||||
/// The following example creates a new in-memory database and connects to it.
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// let database = try Database(store: .inMemory)
|
||||
/// let connection = try database.connect()
|
||||
/// }
|
||||
/// catch {
|
||||
/// // handle error
|
||||
/// }
|
||||
/// ```
|
||||
public final class Database: Sendable {
|
||||
|
||||
/// Duck DB database store type
|
||||
public enum Store {
|
||||
/// A local file based database store
|
||||
case file(at: URL)
|
||||
/// An in-memory database store
|
||||
case inMemory
|
||||
}
|
||||
|
||||
private let ptr = UnsafeMutablePointer<duckdb_database?>.allocate(capacity: 1)
|
||||
|
||||
/// Creates a Duck DB database
|
||||
///
|
||||
/// A DuckDB database can be initilaized using either a local database file or
|
||||
/// using an in-memory store
|
||||
///
|
||||
/// - Note: An in-memory database does not persist data to disk. All data is
|
||||
/// lost when you exit the process.
|
||||
/// - Parameter store: the store to initialize the database with
|
||||
/// - Parameter configuration: the configuration to initialize the database
|
||||
/// with
|
||||
/// - Throws: ``DatabaseError/databaseFailedToInitialize(reason:)`` if the
|
||||
/// database failed to instantiate
|
||||
public convenience init(
|
||||
store: Store = .inMemory, configuration: Configuration? = nil
|
||||
) throws {
|
||||
var fileURL: URL?
|
||||
if case .file(let url) = store {
|
||||
guard url.isFileURL else {
|
||||
throw DatabaseError.databaseFailedToInitialize(
|
||||
reason: "provided URL for database store file must be local")
|
||||
}
|
||||
fileURL = url
|
||||
}
|
||||
try self.init(path: fileURL?.path, config: configuration)
|
||||
}
|
||||
|
||||
private init(path: String?, config: Configuration?) throws {
|
||||
let outError = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>.allocate(capacity: 1)
|
||||
defer { outError.deallocate() }
|
||||
let status = path.withOptionalCString { strPtr in
|
||||
if let config {
|
||||
return config.withCConfiguration { cconfig in
|
||||
duckdb_open_ext(strPtr, ptr, cconfig, outError)
|
||||
}
|
||||
}
|
||||
else {
|
||||
return duckdb_open_ext(strPtr, ptr, nil, outError)
|
||||
}
|
||||
}
|
||||
guard status == .success else {
|
||||
let error = outError.pointee.map { ptr in
|
||||
defer { duckdb_free(ptr) }
|
||||
return String(cString: ptr)
|
||||
}
|
||||
throw DatabaseError.databaseFailedToInitialize(reason: error)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_close(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
/// Creates a connection to the database
|
||||
///
|
||||
/// See ``Connection/init(database:)`` for further discussion
|
||||
///
|
||||
/// - Throws: ``DatabaseError/connectionFailedToInitialize`` if the connection
|
||||
/// could not be instantiated
|
||||
/// - Returns: a database connection
|
||||
public func connect() throws -> Connection {
|
||||
try .init(database: self)
|
||||
}
|
||||
|
||||
func withCDatabase<T>(_ body: (duckdb_database?) throws -> T) rethrows -> T {
|
||||
try body(ptr.pointee)
|
||||
}
|
||||
}
|
||||
55
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/DatabaseError.swift
vendored
Normal file
55
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/DatabaseError.swift
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// A DuckDB database error
|
||||
public enum DatabaseError: Error {
|
||||
/// Provided decimal overflows the internal database representaion
|
||||
case decimalUnrepresentable
|
||||
/// Supplied item could not be appended
|
||||
case appenderFailedToAppendItem(reason: String?)
|
||||
/// Row could not be ended with the appender in its current state
|
||||
case appenderFailedToEndRow(reason: String?)
|
||||
/// Appender could not be flushed in its current state
|
||||
case appenderFailedToFlush(reason: String?)
|
||||
/// Failed to instantiate appender
|
||||
case appenderFailedToInitialize(reason: String?)
|
||||
/// Failed to instantiate connection
|
||||
case connectionFailedToInitialize
|
||||
/// Failed to set flag on database configuration
|
||||
case configurationFailedToSetFlag
|
||||
/// Failed to execute query on connection
|
||||
case connectionQueryError(reason: String?)
|
||||
/// Failed to instantiate database
|
||||
case databaseFailedToInitialize(reason: String?)
|
||||
/// Failed to instantiate prepared statement
|
||||
case preparedStatementFailedToInitialize(reason: String?)
|
||||
/// Failed to bound value to prepared statement
|
||||
case preparedStatementFailedToBindParameter(reason: String?)
|
||||
/// Failed to execute prepared statement query
|
||||
case preparedStatementQueryError(reason: String?)
|
||||
/// Value of type could not be found
|
||||
case valueNotFound(Any.Type)
|
||||
/// Type does not match underlying database type
|
||||
case typeMismatch(Any.Type)
|
||||
}
|
||||
158
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/DatabaseType.swift
vendored
Normal file
158
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/DatabaseType.swift
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// The underlying database type of a DuckDB column
|
||||
///
|
||||
/// DuckDB is a strongly typed database system. As such, every column has a
|
||||
/// single type specified. This type is constant over the entire column. That is
|
||||
/// to say, a column with an underlying type of ``DatabaseType/integer`` will
|
||||
/// only contain ``DatabaseType/integer`` values.
|
||||
///
|
||||
/// DuckDB also supports columns of composite types. For example, it is possible
|
||||
/// to define an array of integers (`INT[]`), which can be cast to `[Int32]`
|
||||
/// within Swift using ``Column/cast(to:)-4376d``. It is also possible to
|
||||
/// define database types as arbitrary structs (`ROW(i INTEGER, j VARCHAR)`),
|
||||
/// which can be cast to their `Decodable` matching Swift type in the same way.
|
||||
public struct DatabaseType: RawRepresentable, Hashable, Equatable {
|
||||
public let rawValue: UInt32
|
||||
public init(rawValue: UInt32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public Types
|
||||
|
||||
public extension DatabaseType {
|
||||
/// Boolean type castable to `Bool`
|
||||
static let boolean = DatabaseType(rawValue: DUCKDB_TYPE_BOOLEAN.rawValue)
|
||||
/// Integer type castable to `Int8`
|
||||
static let tinyint = DatabaseType(rawValue: DUCKDB_TYPE_TINYINT.rawValue)
|
||||
/// Integer type castable to `Int16`
|
||||
static let smallint = DatabaseType(rawValue: DUCKDB_TYPE_SMALLINT.rawValue)
|
||||
/// Integer type castable to `Int32`
|
||||
static let integer = DatabaseType(rawValue: DUCKDB_TYPE_INTEGER.rawValue)
|
||||
/// Integer type castable to `Int64`
|
||||
static let bigint = DatabaseType(rawValue: DUCKDB_TYPE_BIGINT.rawValue)
|
||||
/// Integer type castable to `HugeInt`
|
||||
static let hugeint = DatabaseType(rawValue: DUCKDB_TYPE_HUGEINT.rawValue)
|
||||
/// Integer type castable to `UHugeInt`
|
||||
static let uhugeint = DatabaseType(rawValue: DUCKDB_TYPE_UHUGEINT.rawValue)
|
||||
/// Unsigned integer type castable to `UInt8`
|
||||
static let utinyint = DatabaseType(rawValue: DUCKDB_TYPE_UTINYINT.rawValue)
|
||||
/// Unsigned integer type castable to `UInt16`
|
||||
static let usmallint = DatabaseType(rawValue: DUCKDB_TYPE_USMALLINT.rawValue)
|
||||
/// Unsigned integer type castable to `UInt32`
|
||||
static let uinteger = DatabaseType(rawValue: DUCKDB_TYPE_UINTEGER.rawValue)
|
||||
/// Unsigned integer type castable to `UInt64`
|
||||
static let ubigint = DatabaseType(rawValue: DUCKDB_TYPE_UBIGINT.rawValue)
|
||||
/// Floating point type castable to `Float`
|
||||
static let float = DatabaseType(rawValue: DUCKDB_TYPE_FLOAT.rawValue)
|
||||
/// Floating point type castable to `Double`
|
||||
static let double = DatabaseType(rawValue: DUCKDB_TYPE_DOUBLE.rawValue)
|
||||
/// Timestamp type castable to `Timestamp`
|
||||
static let timestamp = DatabaseType(rawValue: DUCKDB_TYPE_TIMESTAMP.rawValue)
|
||||
/// Timestamp(TZ) type castable to `Timestamp`
|
||||
static let timestampTz = DatabaseType(rawValue: DUCKDB_TYPE_TIMESTAMP_TZ.rawValue)
|
||||
/// Date type castable to `Date`
|
||||
static let date = DatabaseType(rawValue: DUCKDB_TYPE_DATE.rawValue)
|
||||
/// Time type castable to `Time`
|
||||
static let time = DatabaseType(rawValue: DUCKDB_TYPE_TIME.rawValue)
|
||||
/// Time(TZ) type castable to `Time`
|
||||
static let timeTz = DatabaseType(rawValue: DUCKDB_TYPE_TIME_TZ.rawValue)
|
||||
/// Interval type castable to `Interval`
|
||||
static let interval = DatabaseType(rawValue: DUCKDB_TYPE_INTERVAL.rawValue)
|
||||
/// String type castable to `String`
|
||||
static let varchar = DatabaseType(rawValue: DUCKDB_TYPE_VARCHAR.rawValue)
|
||||
/// Data type castable to `Data`
|
||||
static let blob = DatabaseType(rawValue: DUCKDB_TYPE_BLOB.rawValue)
|
||||
/// Decimal type castable to `Decimal`
|
||||
static let decimal = DatabaseType(rawValue: DUCKDB_TYPE_DECIMAL.rawValue)
|
||||
/// Timestamp type castable to `Timestamp`
|
||||
static let timestampS = DatabaseType(rawValue: DUCKDB_TYPE_TIMESTAMP_S.rawValue)
|
||||
/// Timestamp type castable to `Timestamp`
|
||||
static let timestampMS = DatabaseType(rawValue: DUCKDB_TYPE_TIMESTAMP_MS.rawValue)
|
||||
/// Timestamp type castable to `Timestamp`
|
||||
static let timestampNS = DatabaseType(rawValue: DUCKDB_TYPE_TIMESTAMP_NS.rawValue)
|
||||
/// Enum type castable to suitable `Decodable` conforming types
|
||||
static let `enum` = DatabaseType(rawValue: DUCKDB_TYPE_ENUM.rawValue)
|
||||
/// Array type castable to suitable `Decodable` conforming types
|
||||
static let list = DatabaseType(rawValue: DUCKDB_TYPE_LIST.rawValue)
|
||||
/// Struct type castable to suitable `Decodable` conforming types
|
||||
static let `struct` = DatabaseType(rawValue: DUCKDB_TYPE_STRUCT.rawValue)
|
||||
/// Dictionary type castable to suitable `Decodable` conforming types
|
||||
static let map = DatabaseType(rawValue: DUCKDB_TYPE_MAP.rawValue)
|
||||
/// Enum type castable to suitable `Decodable` conforming types
|
||||
static let union = DatabaseType(rawValue: DUCKDB_TYPE_UNION.rawValue)
|
||||
/// UUID type castable to `UUID`
|
||||
static let uuid = DatabaseType(rawValue: DUCKDB_TYPE_UUID.rawValue)
|
||||
}
|
||||
|
||||
// MARK: - Internal Types
|
||||
|
||||
extension DatabaseType {
|
||||
static let invalid = DatabaseType(rawValue: DUCKDB_TYPE_INVALID.rawValue)
|
||||
}
|
||||
|
||||
extension DatabaseType: CustomStringConvertible {
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .boolean: return "\(Self.self).boolean"
|
||||
case .tinyint: return "\(Self.self).tinyint"
|
||||
case .smallint: return "\(Self.self).smallint"
|
||||
case .integer: return "\(Self.self).integer"
|
||||
case .bigint: return "\(Self.self).bigint"
|
||||
case .utinyint: return "\(Self.self).utinyint"
|
||||
case .usmallint: return "\(Self.self).usmallint"
|
||||
case .uinteger: return "\(Self.self).uinteger"
|
||||
case .ubigint: return "\(Self.self).ubigint"
|
||||
case .float: return "\(Self.self).float"
|
||||
case .double: return "\(Self.self).double"
|
||||
case .timestamp: return "\(Self.self).timestamp"
|
||||
case .timestampTz: return "\(Self.self).timestampTZ"
|
||||
case .date: return "\(Self.self).date"
|
||||
case .time: return "\(Self.self).time"
|
||||
case .timeTz: return "\(Self.self).timeTZ"
|
||||
case .interval: return "\(Self.self).interval"
|
||||
case .hugeint: return "\(Self.self).hugeint"
|
||||
case .uhugeint: return "\(Self.self).uhugeint"
|
||||
case .varchar: return "\(Self.self).varchar"
|
||||
case .blob: return "\(Self.self).blob"
|
||||
case .decimal: return "\(Self.self).decimal"
|
||||
case .timestampS: return "\(Self.self).timestampS"
|
||||
case .timestampMS: return "\(Self.self).timestampMS"
|
||||
case .timestampNS: return "\(Self.self).timestampNS"
|
||||
case .`enum`: return "\(Self.self).enum"
|
||||
case .list: return "\(Self.self).list"
|
||||
case .`struct`: return "\(Self.self).struct"
|
||||
case .map: return "\(Self.self).map"
|
||||
case .union: return "\(Self.self).union"
|
||||
case .uuid: return "\(Self.self).uuid"
|
||||
case .invalid: return "\(Self.self).invalid"
|
||||
default: return "\(Self.self).unknown - id: (\(self.rawValue))"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/DuckDB.docc/DuckDB.md
vendored
Normal file
12
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/DuckDB.docc/DuckDB.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# ``DuckDB``
|
||||
|
||||
DuckDB is an in-process SQL OLAP database management system
|
||||
|
||||
## Overview
|
||||
|
||||
DuckDB is a high-performance analytical database system. It is designed to be
|
||||
fast, reliable and easy to use. DuckDB provides a rich SQL dialect, with
|
||||
support far beyond basic SQL. DuckDB supports arbitrary and nested correlated
|
||||
subqueries, window functions, collations, complex types (arrays, structs), and
|
||||
more. For more information on the goals of DuckDB, please refer to the [Why
|
||||
DuckDB page](https://duckdb.org/why_duckdb) on our website.
|
||||
47
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Column+TabularData.swift
vendored
Normal file
47
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Column+TabularData.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.
|
||||
|
||||
#if canImport(TabularData)
|
||||
|
||||
import TabularData
|
||||
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
||||
public extension TabularData.Column {
|
||||
|
||||
/// Creates a tabular data column initialized from a DuckDB column
|
||||
init(_ column: DuckDB.Column<WrappedElement>) {
|
||||
self.init(name: column.name, contents: column)
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
||||
public extension TabularData.AnyColumn {
|
||||
|
||||
/// Creates a type-erased tabular data column from a DuckDB column
|
||||
init<T>(_ column: DuckDB.Column<T>) {
|
||||
self = TabularData.Column(column).eraseToAnyColumn()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
33
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Date+Foundation.swift
vendored
Normal file
33
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Date+Foundation.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
|
||||
|
||||
public extension Foundation.Date {
|
||||
|
||||
/// Creates a date value initialized from a DuckDB Date value
|
||||
init(_ date: Date) {
|
||||
self.init(timeIntervalSince1970: TimeInterval(date.days * 24 * 60 * 60))
|
||||
}
|
||||
}
|
||||
73
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Decimal+IntHuge.swift
vendored
Normal file
73
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Decimal+IntHuge.swift
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
public extension Decimal {
|
||||
|
||||
init(_ source: IntHuge) {
|
||||
let magnitude = source.magnitude
|
||||
let mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)
|
||||
let significantBitCount = magnitude.bitWidth - magnitude.leadingZeroBitCount
|
||||
let length = (significantBitCount + (UInt16.bitWidth - 1)) / UInt16.bitWidth
|
||||
mantissa.0 = UInt16(truncatingIfNeeded: magnitude >> (0 * 16))
|
||||
mantissa.1 = UInt16(truncatingIfNeeded: magnitude >> (1 * 16))
|
||||
mantissa.2 = UInt16(truncatingIfNeeded: magnitude >> (2 * 16))
|
||||
mantissa.3 = UInt16(truncatingIfNeeded: magnitude >> (3 * 16))
|
||||
mantissa.4 = UInt16(truncatingIfNeeded: magnitude >> (4 * 16))
|
||||
mantissa.5 = UInt16(truncatingIfNeeded: magnitude >> (5 * 16))
|
||||
mantissa.6 = UInt16(truncatingIfNeeded: magnitude >> (6 * 16))
|
||||
mantissa.7 = UInt16(truncatingIfNeeded: magnitude >> (7 * 16))
|
||||
self = .init(
|
||||
_exponent: 0,
|
||||
_length: UInt32(length),
|
||||
_isNegative: source.signum() < 0 ? 1 : 0,
|
||||
_isCompact: 0,
|
||||
_reserved: 0,
|
||||
_mantissa: mantissa
|
||||
)
|
||||
}
|
||||
|
||||
init(_ source: UIntHuge) {
|
||||
let mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)
|
||||
let significantBitCount = source.bitWidth - source.leadingZeroBitCount
|
||||
let length = (significantBitCount + (UInt16.bitWidth - 1)) / UInt16.bitWidth
|
||||
mantissa.0 = UInt16(truncatingIfNeeded: source >> (0 * 16))
|
||||
mantissa.1 = UInt16(truncatingIfNeeded: source >> (1 * 16))
|
||||
mantissa.2 = UInt16(truncatingIfNeeded: source >> (2 * 16))
|
||||
mantissa.3 = UInt16(truncatingIfNeeded: source >> (3 * 16))
|
||||
mantissa.4 = UInt16(truncatingIfNeeded: source >> (4 * 16))
|
||||
mantissa.5 = UInt16(truncatingIfNeeded: source >> (5 * 16))
|
||||
mantissa.6 = UInt16(truncatingIfNeeded: source >> (6 * 16))
|
||||
mantissa.7 = UInt16(truncatingIfNeeded: source >> (7 * 16))
|
||||
self = .init(
|
||||
_exponent: 0,
|
||||
_length: UInt32(length),
|
||||
_isNegative: 0,
|
||||
_isCompact: 0,
|
||||
_reserved: 0,
|
||||
_mantissa: mantissa
|
||||
)
|
||||
}
|
||||
}
|
||||
41
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Time+Foundation.swift
vendored
Normal file
41
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Time+Foundation.swift
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
public extension Foundation.Date {
|
||||
|
||||
/// Creates a date value initialized from a DuckDB Time value
|
||||
init(_ date: Time) {
|
||||
self.init(timeIntervalSince1970: TimeInterval(date.microseconds) * 1e-3)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Time {
|
||||
|
||||
/// Creates a time value initialized from a Foundation Date
|
||||
init(_ date: Foundation.Date) {
|
||||
self.init(microseconds: Int64(date.timeIntervalSince1970 * 1e3))
|
||||
}
|
||||
}
|
||||
41
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Timestamp+Foundation.swift
vendored
Normal file
41
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Extensions/Timestamp+Foundation.swift
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
public extension Foundation.Date {
|
||||
|
||||
/// Creates a date value initialized from a DuckDB Timestamp value
|
||||
init(_ timestamp: Timestamp) {
|
||||
self.init(timeIntervalSince1970: TimeInterval(timestamp.microseconds) * 1e-3)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Timestamp {
|
||||
|
||||
/// Creates a timestamp value initialized from a Foundation Date
|
||||
init(_ date: Foundation.Date) {
|
||||
self.init(microseconds: Int64(date.timeIntervalSince1970 * 1e3))
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
163
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/LogicalType.swift
vendored
Normal file
163
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/LogicalType.swift
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
public final class LogicalType {
|
||||
|
||||
private let ptr = UnsafeMutablePointer<duckdb_logical_type?>.allocate(capacity: 1)
|
||||
|
||||
init(_ body: () -> duckdb_logical_type?) {
|
||||
self.ptr.pointee = body()
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_destroy_logical_type(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
/// The primitive type represented by this logical type.
|
||||
public var dataType: DatabaseType {
|
||||
let ctypeid = duckdb_get_type_id(ptr.pointee)
|
||||
return ctypeid.asTypeID
|
||||
}
|
||||
|
||||
/// The primitive type representing the internal storage type, which is equivalent
|
||||
/// to ``dataType``, except for when the type is an enum.
|
||||
public var underlyingDataType: DatabaseType {
|
||||
guard dataType == .enum else { return dataType }
|
||||
let ctypeid = duckdb_enum_internal_type(ptr.pointee)
|
||||
return ctypeid.asTypeID
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Decimal
|
||||
|
||||
public extension LogicalType {
|
||||
|
||||
/// Properties associated with a decimal type
|
||||
struct DecimalProperties {
|
||||
let width: UInt8
|
||||
let scale: UInt8
|
||||
let storageType: DatabaseType
|
||||
}
|
||||
|
||||
/// Properties associated with a decimal type. For all other types, returns `nil`
|
||||
var decimalProperties: DecimalProperties? {
|
||||
guard dataType == .decimal else { return nil }
|
||||
let internalStorageType = duckdb_decimal_internal_type(ptr.pointee)
|
||||
return .init(
|
||||
width: duckdb_decimal_width(ptr.pointee),
|
||||
scale: duckdb_decimal_scale(ptr.pointee),
|
||||
storageType: internalStorageType.asTypeID
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Struct
|
||||
|
||||
public extension LogicalType {
|
||||
|
||||
static let structCompatibleTypes = [DatabaseType.struct, .map]
|
||||
|
||||
/// Properties associated with a struct type
|
||||
struct StructMemberProperties {
|
||||
let name: String
|
||||
let type: LogicalType
|
||||
}
|
||||
|
||||
/// Properties associated with a struct type. For all other types, returns `nil`
|
||||
var structMemberProperties: [StructMemberProperties]? {
|
||||
guard Self.structCompatibleTypes.contains(dataType) else { return nil }
|
||||
let memberCount = duckdb_struct_type_child_count(ptr.pointee)
|
||||
var properties = [StructMemberProperties]()
|
||||
properties.reserveCapacity(Int(memberCount))
|
||||
for i in 0..<memberCount {
|
||||
let cStr = duckdb_struct_type_child_name(ptr.pointee, i)!
|
||||
properties.append(StructMemberProperties(
|
||||
name: String(cString: cStr),
|
||||
type: LogicalType { duckdb_struct_type_child_type(ptr.pointee, i) }
|
||||
))
|
||||
duckdb_free(cStr)
|
||||
}
|
||||
return properties
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Union
|
||||
|
||||
public extension LogicalType {
|
||||
|
||||
/// Properties associated with a union type
|
||||
struct UnionMemberProperties {
|
||||
let name: String
|
||||
let type: LogicalType
|
||||
}
|
||||
|
||||
/// Properties associated with a union type. For all other types, returns `nil`
|
||||
var unionMemberProperties: [UnionMemberProperties]? {
|
||||
guard dataType == .union else { return nil }
|
||||
let memberCount = duckdb_union_type_member_count(ptr.pointee)
|
||||
var properties = [UnionMemberProperties]()
|
||||
properties.reserveCapacity(Int(memberCount))
|
||||
for i in 0..<memberCount {
|
||||
let cStr = duckdb_union_type_member_name(ptr.pointee, i)!
|
||||
properties.append(UnionMemberProperties(
|
||||
name: String(cString: cStr),
|
||||
type: LogicalType { duckdb_union_type_member_type(ptr.pointee, i) }
|
||||
))
|
||||
duckdb_free(cStr)
|
||||
}
|
||||
return properties
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - List
|
||||
|
||||
public extension LogicalType {
|
||||
|
||||
/// Child type of a list type. For all other types, returns `nil`
|
||||
var listChildType: LogicalType? {
|
||||
guard dataType == .list else { return nil }
|
||||
return LogicalType { duckdb_list_type_child_type(ptr.pointee) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Map
|
||||
|
||||
public extension LogicalType {
|
||||
|
||||
/// Key type of a map type. For all other types, returns `nil`
|
||||
var mapKeyType: LogicalType? {
|
||||
guard dataType == .map else { return nil }
|
||||
return LogicalType { duckdb_map_type_key_type(ptr.pointee) }
|
||||
}
|
||||
|
||||
/// Value type of a map type. For all other types, returns `nil`
|
||||
var mapValueType: LogicalType? {
|
||||
guard dataType == .map else { return nil }
|
||||
return LogicalType { duckdb_map_type_value_type(ptr.pointee) }
|
||||
}
|
||||
}
|
||||
458
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/PreparedStatement.swift
vendored
Normal file
458
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/PreparedStatement.swift
vendored
Normal file
@@ -0,0 +1,458 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// An object representing a DuckDB prepared statement
|
||||
///
|
||||
/// A prepared statement is a parameterized query. The query is prepared with
|
||||
/// question marks (`?`) or dollar symbols (`$1`) indicating the parameters of
|
||||
/// the query. Values can then be bound to these parameters, after which the
|
||||
/// prepared statement can be executed using those parameters. A single query
|
||||
/// can be prepared once and executed many times.
|
||||
///
|
||||
/// Prepared statements are useful to:
|
||||
///
|
||||
/// - Easily supply parameters to functions while avoiding string
|
||||
/// concatenation/SQL injection attacks.
|
||||
/// - Speed up queries that will be executed many times with different
|
||||
/// parameters.
|
||||
///
|
||||
/// The following example creates a prepared statement that allows parameters
|
||||
/// to be bound in two positions within a 'select' statement. The prepared
|
||||
/// statement is finally executed by calling ``PreparedStatement/execute()``.
|
||||
///
|
||||
/// ```swift
|
||||
/// let connection: Connection = ...
|
||||
/// let statement = try PreparedStatement(
|
||||
/// connection: connection,
|
||||
/// query: "SELECT $1 from $2"
|
||||
/// )
|
||||
/// try statement.bind("last_name")
|
||||
/// try statement.bind("employees")
|
||||
/// // executes 'SELECT last_name from employees'
|
||||
/// let result = try statement.execute()
|
||||
/// ```
|
||||
public final class PreparedStatement {
|
||||
|
||||
/// The number of parameters to which values can be bound
|
||||
public var parameterCount: Int { Int(duckdb_nparams(ptr.pointee)) }
|
||||
|
||||
private let connection: Connection
|
||||
private let ptr = UnsafeMutablePointer<duckdb_prepared_statement?>.allocate(capacity: 1)
|
||||
|
||||
/// Creates a new prepared statement for a given connection and query
|
||||
///
|
||||
/// The query is prepared with question marks (`?`) or dollar symbols (`$1`)
|
||||
/// indicating the parameters of the query.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter connection: the connection on which the prepared stement will
|
||||
/// execute
|
||||
/// - Parameter query: the parameterized query
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToInitialize(reason:)``
|
||||
/// if there is a problem with the query or connection
|
||||
public init(connection: Connection, query: String) throws {
|
||||
self.connection = connection
|
||||
let status = query.withCString { queryStPtr in
|
||||
connection.withCConnection { duckdb_prepare($0, queryStPtr, ptr) }
|
||||
}
|
||||
guard .success == status else {
|
||||
throw DatabaseError.preparedStatementFailedToInitialize(reason: preparedStatementError())
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_destroy_prepare(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
|
||||
/// Executes the prepared statement
|
||||
///
|
||||
/// Issues the parameterized query to the database using the values previously
|
||||
/// bound via the `bind(_:at:)` set of functions.
|
||||
///
|
||||
/// - Throws: ``DatabaseError/preparedStatementQueryError(reason:)``
|
||||
public func execute() throws -> ResultSet {
|
||||
try ResultSet(prepared: self)
|
||||
}
|
||||
|
||||
func parameterType(at index: Int) -> DatabaseType {
|
||||
let cparamtype = duckdb_param_type(ptr.pointee, DBInt(index))
|
||||
return cparamtype.asTypeID
|
||||
}
|
||||
|
||||
func clearBindings() {
|
||||
duckdb_clear_bindings(ptr.pointee)
|
||||
}
|
||||
|
||||
func withCPreparedStatement<T>(_ body: (duckdb_prepared_statement?) throws -> T) rethrows -> T {
|
||||
try body(ptr.pointee)
|
||||
}
|
||||
}
|
||||
|
||||
public extension PreparedStatement {
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Bool?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_boolean(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Int8?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_int8(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Int16?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_int16(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Int32?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_int32(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Int64?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_int64(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: IntHuge?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_hugeint(ptr.pointee, .init(index), .init(value)) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: UIntHuge?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_uhugeint(ptr.pointee, .init(index), .init(value)) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Decimal?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_decimal(ptr.pointee, .init(index), try .init(value)) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: UInt8?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_uint8(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: UInt16?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_uint16(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: UInt32?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_uint32(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: UInt64?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_uint64(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Float?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_float(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Double?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_double(ptr.pointee, .init(index), value) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Date?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_date(ptr.pointee, .init(index), .init(date: value)) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Time?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand { duckdb_bind_time(ptr.pointee, .init(index), .init(time: value)) }
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Timestamp?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand {
|
||||
duckdb_bind_timestamp(ptr.pointee, .init(index), .init(timestamp: value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Interval?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand {
|
||||
duckdb_bind_interval(ptr.pointee, .init(index), .init(interval: value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: String?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
let data = value.data(using: .utf8)!
|
||||
try withThrowingCommand {
|
||||
data.withUnsafeBytes { dataPtr in
|
||||
duckdb_bind_varchar_length(
|
||||
ptr.pointee, .init(index), dataPtr.baseAddress, .init(dataPtr.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a value of the given type at the specified parameter index
|
||||
///
|
||||
/// Sets the value that will be used for the next call to ``execute()``.
|
||||
///
|
||||
/// - Important: Prepared statement parameters use one-based indexing
|
||||
/// - Parameter value: the value to bind
|
||||
/// - Parameter index: the one-based parameter index
|
||||
/// - Throws: ``DatabaseError/preparedStatementFailedToBindParameter(reason:)``
|
||||
/// if there is a type-mismatch between the value being bound and the
|
||||
/// underlying column type
|
||||
func bind(_ value: Data?, at index: Int) throws {
|
||||
guard let value = try unwrapValueOrBindNull(value, at: index) else { return }
|
||||
try withThrowingCommand {
|
||||
value.withUnsafeBytes { dataPtr in
|
||||
duckdb_bind_blob(
|
||||
ptr.pointee, .init(index), dataPtr.baseAddress, .init(dataPtr.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension PreparedStatement {
|
||||
|
||||
func bindNullValue(at index: Int) throws {
|
||||
try withThrowingCommand { duckdb_bind_null(ptr.pointee, .init(index)) }
|
||||
}
|
||||
|
||||
func unwrapValueOrBindNull<T>(_ value: T?, at index: Int) throws -> T? {
|
||||
guard let value else {
|
||||
try bindNullValue(at: index)
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func withThrowingCommand(_ body: () throws -> duckdb_state) throws {
|
||||
let state = try body()
|
||||
guard state == .success else {
|
||||
throw DatabaseError.preparedStatementFailedToBindParameter(reason: preparedStatementError())
|
||||
}
|
||||
}
|
||||
|
||||
func preparedStatementError() -> String? {
|
||||
duckdb_prepare_error(ptr.pointee).map(String.init(cString:))
|
||||
}
|
||||
}
|
||||
251
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/ResultSet.swift
vendored
Normal file
251
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/ResultSet.swift
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// An object representing a DuckDB result set
|
||||
///
|
||||
/// A DuckDB result set contains the data returned from the database after a
|
||||
/// successful query.
|
||||
///
|
||||
/// A result set is organized into vertical table slices called columns. Each
|
||||
/// column of the result set is accessible by calling the ``subscript(_:)``
|
||||
/// method of the result.
|
||||
///
|
||||
/// Elements of a column can be accessed by casting the column to the native
|
||||
/// Swift type that matches the underlying database column type. See ``Column``
|
||||
/// for further discussion.
|
||||
public struct ResultSet: Sendable {
|
||||
|
||||
/// The number of chunks in the result set
|
||||
public var chunkCount: DBInt { chunkStorage.chunkCount }
|
||||
/// The number of columns in the result set
|
||||
public var columnCount: DBInt { storage.columnCount }
|
||||
/// The total number of rows in the result set
|
||||
public var rowCount: DBInt { chunkStorage.totalRowCount }
|
||||
|
||||
private let storage: ResultStorage
|
||||
private let chunkStorage: ChunkStorage
|
||||
|
||||
init(connection: Connection, sql: String) throws {
|
||||
self.storage = try ResultStorage(connection: connection, sql: sql)
|
||||
self.chunkStorage = ChunkStorage(resultStorage: storage)
|
||||
}
|
||||
|
||||
init(prepared: PreparedStatement) throws {
|
||||
self.storage = try ResultStorage(prepared: prepared)
|
||||
self.chunkStorage = ChunkStorage(resultStorage: storage)
|
||||
}
|
||||
|
||||
/// Returns a `Void` typed column for the given column index
|
||||
///
|
||||
/// A `Void` typed column can be cast to a column matching the underlying
|
||||
/// database representation using ``Column/cast(to:)-4376d``. See ``Column``
|
||||
/// for further discussion.
|
||||
///
|
||||
/// - Parameter columnIndex: the index of the column in the result set
|
||||
/// - Returns: a `Void` typed column
|
||||
public func column(at columnIndex: DBInt) -> Column<Void> {
|
||||
precondition(columnIndex < columnCount)
|
||||
return Column(result: self, columnIndex: columnIndex) { $0.unwrapNull() ? nil : () }
|
||||
}
|
||||
|
||||
/// The underlying column name for the given column index
|
||||
///
|
||||
/// - Parameter columnIndex: the index of the column in the result set
|
||||
/// - Returns: the name of the column
|
||||
public func columnName(at columnIndex: DBInt) -> String {
|
||||
storage.columnName(at: columnIndex)
|
||||
}
|
||||
|
||||
/// The index of the given column name
|
||||
///
|
||||
/// - Parameter columnName: the name of the column in the result set
|
||||
/// - Returns: the index of the column
|
||||
/// - Complexity: O(n)
|
||||
public func index(forColumnName columnName: String) -> DBInt? {
|
||||
for i in 0..<columnCount {
|
||||
if self.columnName(at: i) == columnName {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func columnDataType(at index: DBInt) -> DatabaseType {
|
||||
storage.columnDataType(at: index)
|
||||
}
|
||||
|
||||
func columnLogicalType(at index: DBInt) -> LogicalType {
|
||||
storage.columnLogicalType(at: index)
|
||||
}
|
||||
|
||||
func element(forColumn columnIndex: DBInt, at index: DBInt) -> Vector.Element {
|
||||
var chunkIndex = DBInt.zero
|
||||
var chunkRowOffset = DBInt.zero
|
||||
while chunkIndex < chunkCount {
|
||||
let chunk = chunkStorage[Int(chunkIndex)]
|
||||
let chunkCount = chunk.count
|
||||
if index < chunkRowOffset + chunkCount {
|
||||
return chunk.withVector(at: columnIndex) { vector in
|
||||
vector[Int(index - chunkRowOffset)]
|
||||
}
|
||||
}
|
||||
else {
|
||||
chunkIndex += 1
|
||||
chunkRowOffset += chunkCount
|
||||
}
|
||||
}
|
||||
preconditionFailure("item out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Collection conformance
|
||||
|
||||
extension ResultSet: RandomAccessCollection {
|
||||
|
||||
public typealias Element = Column<Void>
|
||||
|
||||
public struct Iterator: IteratorProtocol {
|
||||
|
||||
private let result: ResultSet
|
||||
private var position: DBInt
|
||||
|
||||
init(result: ResultSet) {
|
||||
self.result = result
|
||||
self.position = result.startIndex
|
||||
}
|
||||
|
||||
public mutating func next() -> Element? {
|
||||
guard position < result.endIndex else { return nil }
|
||||
defer { position += 1 }
|
||||
return .some(result[position])
|
||||
}
|
||||
}
|
||||
|
||||
public var startIndex: DBInt { 0 }
|
||||
public var endIndex: DBInt { columnCount }
|
||||
|
||||
public subscript(position: DBInt) -> Column<Void> {
|
||||
column(at: position)
|
||||
}
|
||||
|
||||
public func makeIterator() -> Iterator {
|
||||
Iterator(result: self)
|
||||
}
|
||||
|
||||
public func index(after i: DBInt) -> DBInt { i + 1 }
|
||||
public func index(before i: DBInt) -> DBInt { i - 1 }
|
||||
}
|
||||
|
||||
// MARK: - Debug Description
|
||||
|
||||
extension ResultSet: CustomDebugStringConvertible {
|
||||
|
||||
public var debugDescription: String {
|
||||
let summary = "chunks: \(chunkCount); rows: \(rowCount); columns: \(columnCount); layout:"
|
||||
var columns = [String]()
|
||||
for i in 0..<columnCount {
|
||||
let name = columnName(at: i)
|
||||
let type = columnDataType(at: i).description.uppercased()
|
||||
columns.append("\t\(name) \(type)")
|
||||
}
|
||||
return "<\(Self.self): { \(summary) (\n\(columns.joined(separator: ",\n"))\n);>"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Utilities
|
||||
|
||||
fileprivate final class ResultStorage: Sendable {
|
||||
|
||||
let columnCount: DBInt
|
||||
|
||||
private let ptr = UnsafeMutablePointer<duckdb_result>.allocate(capacity: 1)
|
||||
|
||||
init(connection: Connection, sql: String) throws {
|
||||
let status = sql.withCString { [ptr] queryStrPtr in
|
||||
connection.withCConnection { duckdb_query($0, queryStrPtr, ptr) }
|
||||
}
|
||||
guard status == .success else {
|
||||
let error = duckdb_result_error(ptr).map(String.init(cString:))
|
||||
throw DatabaseError.connectionQueryError(reason: error)
|
||||
}
|
||||
self.columnCount = duckdb_column_count(ptr)
|
||||
}
|
||||
|
||||
init(prepared: PreparedStatement) throws {
|
||||
let status = prepared.withCPreparedStatement { [ptr] in duckdb_execute_prepared($0, ptr) }
|
||||
guard status == .success else {
|
||||
let error = duckdb_result_error(ptr).map(String.init(cString:))
|
||||
throw DatabaseError.preparedStatementQueryError(reason: error)
|
||||
}
|
||||
self.columnCount = duckdb_column_count(ptr)
|
||||
}
|
||||
|
||||
func columnName(at columnIndex: DBInt) -> String {
|
||||
String(cString: duckdb_column_name(ptr, columnIndex))
|
||||
}
|
||||
|
||||
func columnDataType(at index: DBInt) -> DatabaseType {
|
||||
let dataType = duckdb_column_type(ptr, index)
|
||||
return DatabaseType(rawValue: dataType.rawValue)
|
||||
}
|
||||
|
||||
func columnLogicalType(at index: DBInt) -> LogicalType {
|
||||
return LogicalType { duckdb_column_logical_type(ptr, index) }
|
||||
}
|
||||
|
||||
func withCResult<T>(_ body: (duckdb_result) throws -> T) rethrows -> T {
|
||||
try body(ptr.pointee)
|
||||
}
|
||||
|
||||
deinit {
|
||||
duckdb_destroy_result(ptr)
|
||||
ptr.deallocate()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate final class ChunkStorage: Sendable {
|
||||
|
||||
let chunkCount: DBInt
|
||||
let totalRowCount: DBInt
|
||||
private let chunks: [DataChunk]
|
||||
|
||||
init(resultStorage: ResultStorage) {
|
||||
let chunkCount = resultStorage.withCResult { duckdb_result_chunk_count($0) }
|
||||
var chunks = [DataChunk]()
|
||||
var totalRowCount = DBInt(0)
|
||||
for i in 0 ..< chunkCount {
|
||||
let chunk = resultStorage.withCResult { DataChunk(cresult: $0, index: i) }
|
||||
chunks.append(chunk)
|
||||
totalRowCount += chunk.count
|
||||
}
|
||||
self.chunks = chunks
|
||||
self.chunkCount = chunkCount
|
||||
self.totalRowCount = totalRowCount
|
||||
}
|
||||
|
||||
subscript(position: Int) -> DataChunk { chunks[position] }
|
||||
}
|
||||
69
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Date.swift
vendored
Normal file
69
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Date.swift
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// A date in the Gregorian calendar
|
||||
///
|
||||
/// A date specifies a combination of year, month and day. DuckDB follows the
|
||||
/// SQL standard’s lead by counting dates exclusively in the Gregorian calendar,
|
||||
/// even for years before that calendar was in use.
|
||||
public struct Date: Hashable, Equatable, Codable, Sendable {
|
||||
/// days since the unix date epoch `1970-01-01`
|
||||
public var days: Int32
|
||||
}
|
||||
|
||||
public extension Date {
|
||||
|
||||
/// The components of ``Date`` decomposed into its constituent parts
|
||||
///
|
||||
/// A type to facilate the conversion between nominal units of years, months
|
||||
/// and days into the underlying DuckDB ``Date`` representation of days since
|
||||
/// `1970-01-01`.
|
||||
struct Components: Hashable, Equatable {
|
||||
public var year: Int32
|
||||
public var month: Int8
|
||||
public var day: Int8
|
||||
}
|
||||
|
||||
/// Creates a new instance from the given date components
|
||||
///
|
||||
/// - Parameter components: the date components of the instance
|
||||
init(components: Components) {
|
||||
let cdatestruct = duckdb_to_date(duckdb_date_struct(components: components))
|
||||
self = cdatestruct.asDate
|
||||
}
|
||||
|
||||
/// Date components
|
||||
var components: Components { Components(self) }
|
||||
}
|
||||
|
||||
private extension Date.Components {
|
||||
|
||||
init(_ date: Date) {
|
||||
let cdate = duckdb_date(date: date)
|
||||
let cdatestruct = duckdb_from_date(cdate)
|
||||
self = cdatestruct.asDateComponents
|
||||
}
|
||||
}
|
||||
1370
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/IntHuge.swift
vendored
Normal file
1370
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/IntHuge.swift
vendored
Normal file
File diff suppressed because it is too large
Load Diff
57
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Interval.swift
vendored
Normal file
57
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Interval.swift
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// A period of time
|
||||
///
|
||||
/// Intervals represent a period of time. This period can be measured in a
|
||||
/// variety of units, for example years, days, or seconds.
|
||||
public struct Interval: Hashable, Equatable, Sendable {
|
||||
public var months: Int32
|
||||
public var days: Int32
|
||||
public var microseconds: Int64
|
||||
}
|
||||
|
||||
extension Interval {
|
||||
|
||||
init(
|
||||
years: Int32,
|
||||
months: Int32,
|
||||
days: Int32,
|
||||
hours: Int32,
|
||||
minutes: Int32,
|
||||
seconds: Int32,
|
||||
microseconds: Int64
|
||||
) {
|
||||
let hours_ms = Int64(hours) * 60 * 60 * 1_000_000
|
||||
let minutes_ms = Int64(minutes) * 60 * 1_000_000
|
||||
let seconds_ms = Int64(seconds) * 1_000_000
|
||||
self.init(
|
||||
months: (years * 12) + months,
|
||||
days: days,
|
||||
microseconds: hours_ms + minutes_ms + seconds_ms + microseconds
|
||||
)
|
||||
}
|
||||
}
|
||||
76
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Time.swift
vendored
Normal file
76
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Time.swift
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// A point in absolute time
|
||||
///
|
||||
/// Time represents points in absolute time, usually called instants.
|
||||
/// DuckDB represents instants as the number of microseconds (µs) since
|
||||
/// `1970-01-01 00:00:00+00`.
|
||||
public struct Time: Hashable, Equatable, Sendable {
|
||||
/// microseconds (µs) since `1970-01-01 00:00:00+00`.
|
||||
public var microseconds: Int64
|
||||
}
|
||||
|
||||
public extension Time {
|
||||
|
||||
/// The components of ``Time`` decomposed into its constituent parts
|
||||
///
|
||||
/// A type to facilate the conversion between nominal units of hours,
|
||||
/// minutes, seconds and microseconds into the underlying DuckDB
|
||||
/// ``Time`` representation of microseconds (µs) since
|
||||
/// `1970-01-01 00:00:00+00`.
|
||||
struct Components: Hashable, Equatable {
|
||||
public var hour: Int8
|
||||
public var minute: Int8
|
||||
public var second: Int8
|
||||
public var microsecond: Int32
|
||||
}
|
||||
|
||||
/// Creates a new instance from the given time components
|
||||
///
|
||||
/// - Parameter components: the time components of the instance
|
||||
init(components: Components) {
|
||||
let ctimestruct = duckdb_to_time(duckdb_time_struct(components: components))
|
||||
self = ctimestruct.asTime
|
||||
}
|
||||
|
||||
/// Time components
|
||||
var components: Components { Components(self) }
|
||||
}
|
||||
|
||||
private extension Time.Components {
|
||||
|
||||
init(_ time: Time) {
|
||||
let ctime = duckdb_time(time: time)
|
||||
let ctimestruct = duckdb_from_time(ctime)
|
||||
self = ctimestruct.asTimeComponents
|
||||
}
|
||||
}
|
||||
|
||||
public struct TimeTz: Hashable, Equatable, Sendable {
|
||||
public var time: Time
|
||||
public var offset: Int32
|
||||
}
|
||||
87
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Timestamp.swift
vendored
Normal file
87
external/duckdb/tools/swift/duckdb-swift/Sources/DuckDB/Types/Timestamp.swift
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// A point in absolute time
|
||||
///
|
||||
/// Timestamps represent points in absolute time, usually called instants.
|
||||
/// DuckDB represents instants as the number of microseconds (µs) since
|
||||
/// `1970-01-01 00:00:00+00`.
|
||||
public struct Timestamp: Hashable, Equatable, Codable, Sendable {
|
||||
/// microseconds (µs) since `1970-01-01 00:00:00+00`.
|
||||
public var microseconds: Int64
|
||||
}
|
||||
|
||||
public extension Timestamp {
|
||||
|
||||
/// The components of a ``Timestamp`` decomposed into its constituent parts
|
||||
///
|
||||
/// A type to facilate the conversion between nominal units of year, month,
|
||||
/// day, hours, minutes, seconds and microseconds into the underlying DuckDB
|
||||
/// ``Timestamp`` representation of microseconds (µs) since
|
||||
/// `1970-01-01 00:00:00+00`.
|
||||
struct Components: Hashable, Equatable {
|
||||
/// Date components
|
||||
public var date: Date.Components
|
||||
/// Time components
|
||||
public var time: Time.Components
|
||||
}
|
||||
|
||||
/// Creates a new instance from the given timestamp components
|
||||
///
|
||||
/// - Parameter components: the components of the timestamp to be instantiated
|
||||
init(components: Components) {
|
||||
let ctimestampstruct = duckdb_to_timestamp(duckdb_timestamp_struct(components: components))
|
||||
self = ctimestampstruct.asTimestamp
|
||||
}
|
||||
|
||||
/// Timestamp components
|
||||
var components: Components { Components(self) }
|
||||
}
|
||||
|
||||
private extension Timestamp.Components {
|
||||
|
||||
init(_ timestamp: Timestamp) {
|
||||
let ctimestamp = duckdb_timestamp(timestamp: timestamp)
|
||||
let ctimestampstruct = duckdb_from_timestamp(ctimestamp)
|
||||
self = ctimestampstruct.asTimestampComponents
|
||||
}
|
||||
}
|
||||
|
||||
extension Timestamp.Components {
|
||||
|
||||
init(
|
||||
year: Int32,
|
||||
month: Int8,
|
||||
day: Int8,
|
||||
hour: Int8,
|
||||
minute: Int8,
|
||||
second: Int8,
|
||||
microsecond: Int32
|
||||
) {
|
||||
self.date = .init(year: year, month: month, day: day)
|
||||
self.time = .init(hour: hour, minute: minute, second: second, microsecond: microsecond)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user