Merge branch 'feature/multicast-delegate' into develop
This commit is contained in:
@@ -21,26 +21,26 @@ extension Connection {
|
||||
/// - ``init(code:message:)``
|
||||
public struct Error: Swift.Error, Equatable, CustomStringConvertible {
|
||||
// MARK: - Properties
|
||||
|
||||
|
||||
/// The database engine error code.
|
||||
///
|
||||
/// This code indicates the specific error returned by SQLite during an operation.
|
||||
/// For a full list of possible error codes, see:
|
||||
/// [SQLite Result and Error Codes](https://www.sqlite.org/rescode.html).
|
||||
public let code: Int32
|
||||
|
||||
|
||||
/// A human-readable error message describing the failure.
|
||||
public let message: String
|
||||
|
||||
|
||||
/// A textual representation of the error.
|
||||
///
|
||||
/// Combines the error code and message into a single descriptive string.
|
||||
public var description: String {
|
||||
"Connection.Error code: \(code) message: \(message)"
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
|
||||
/// Creates an error with the given code and message.
|
||||
///
|
||||
/// - Parameters:
|
||||
@@ -50,7 +50,7 @@ extension Connection {
|
||||
self.code = code
|
||||
self.message = message
|
||||
}
|
||||
|
||||
|
||||
/// Creates an error by extracting details from a SQLite connection.
|
||||
///
|
||||
/// - Parameter connection: A pointer to the SQLite connection.
|
||||
|
||||
@@ -5,10 +5,7 @@ public final class Connection: ConnectionProtocol {
|
||||
// MARK: - Private Properties
|
||||
|
||||
private let connection: OpaquePointer
|
||||
|
||||
// MARK: - Delegation
|
||||
|
||||
public weak var delegate: (any ConnectionDelegate)?
|
||||
fileprivate var delegates = [DelegateBox]()
|
||||
|
||||
// MARK: - Connection State
|
||||
|
||||
@@ -61,6 +58,17 @@ public final class Connection: ConnectionProtocol {
|
||||
sqlite3_close_v2(connection)
|
||||
}
|
||||
|
||||
// MARK: - Delegation
|
||||
|
||||
public func addDelegate(_ delegate: ConnectionDelegate) {
|
||||
delegates.removeAll { $0.delegate == nil }
|
||||
delegates.append(.init(delegate: delegate))
|
||||
}
|
||||
|
||||
public func removeDelegate(_ delegate: ConnectionDelegate) {
|
||||
delegates.removeAll { $0.delegate == nil || $0.delegate === delegate }
|
||||
}
|
||||
|
||||
// MARK: - Custom SQL Functions
|
||||
|
||||
public func add(function: Function.Type) throws(Error) {
|
||||
@@ -111,6 +119,16 @@ public final class Connection: ConnectionProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension Connection {
|
||||
class DelegateBox {
|
||||
weak var delegate: ConnectionDelegate?
|
||||
|
||||
init(delegate: ConnectionDelegate? = nil) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
private func traceCallback(
|
||||
@@ -124,16 +142,18 @@ private func traceCallback(
|
||||
.fromOpaque(ctx)
|
||||
.takeUnretainedValue()
|
||||
|
||||
if let delegate = connection.delegate {
|
||||
guard let stmt = OpaquePointer(p),
|
||||
let pSql = sqlite3_expanded_sql(stmt),
|
||||
let xSql = x?.assumingMemoryBound(to: CChar.self)
|
||||
else { return SQLITE_OK }
|
||||
|
||||
let pSqlString = String(cString: pSql)
|
||||
let xSqlString = String(cString: xSql)
|
||||
let trace = (xSqlString, pSqlString)
|
||||
delegate.connection(connection, trace: trace)
|
||||
guard !connection.delegates.isEmpty,
|
||||
let stmt = OpaquePointer(p),
|
||||
let pSql = sqlite3_expanded_sql(stmt),
|
||||
let xSql = x?.assumingMemoryBound(to: CChar.self)
|
||||
else { return SQLITE_OK }
|
||||
|
||||
let pSqlString = String(cString: pSql)
|
||||
let xSqlString = String(cString: xSql)
|
||||
let trace = (xSqlString, pSqlString)
|
||||
|
||||
for box in connection.delegates {
|
||||
box.delegate?.connection(connection, trace: trace)
|
||||
}
|
||||
|
||||
return SQLITE_OK
|
||||
@@ -151,7 +171,7 @@ private func updateHookCallback(
|
||||
.fromOpaque(ctx)
|
||||
.takeUnretainedValue()
|
||||
|
||||
if let delegate = connection.delegate {
|
||||
if !connection.delegates.isEmpty {
|
||||
guard let dName = dName, let tName = tName else { return }
|
||||
|
||||
let dbName = String(cString: dName)
|
||||
@@ -169,18 +189,21 @@ private func updateHookCallback(
|
||||
return
|
||||
}
|
||||
|
||||
delegate.connection(connection, didUpdate: updateAction)
|
||||
for box in connection.delegates {
|
||||
box.delegate?.connection(connection, didUpdate: updateAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func commitHookCallback(_ ctx: UnsafeMutableRawPointer?) -> Int32 {
|
||||
guard let ctx = ctx else { return SQLITE_OK }
|
||||
let connection = Unmanaged<Connection>
|
||||
.fromOpaque(ctx)
|
||||
.takeUnretainedValue()
|
||||
|
||||
do {
|
||||
guard let ctx = ctx else { return SQLITE_OK }
|
||||
let connection = Unmanaged<Connection>
|
||||
.fromOpaque(ctx)
|
||||
.takeUnretainedValue()
|
||||
if let delegate = connection.delegate {
|
||||
try delegate.connectionDidCommit(connection)
|
||||
for box in connection.delegates {
|
||||
try box.delegate?.connectionDidCommit(connection)
|
||||
}
|
||||
return SQLITE_OK
|
||||
} catch {
|
||||
@@ -193,7 +216,8 @@ private func rollbackHookCallback(_ ctx: UnsafeMutableRawPointer?) {
|
||||
let connection = Unmanaged<Connection>
|
||||
.fromOpaque(ctx)
|
||||
.takeUnretainedValue()
|
||||
if let delegate = connection.delegate {
|
||||
delegate.connectionDidRollback(connection)
|
||||
|
||||
for box in connection.delegates {
|
||||
box.delegate?.connectionDidRollback(connection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,7 @@ private func xFinal(_ ctx: OpaquePointer?) {
|
||||
let description = error.localizedDescription
|
||||
let message = "Error executing function '\(name)': \(description)"
|
||||
sqlite3_result_error(ctx, message, -1)
|
||||
sqlite3_result_error_code(ctx, SQLITE_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ private func xFunc(
|
||||
let description = error.localizedDescription
|
||||
let message = "Error executing function '\(name)': \(description)"
|
||||
sqlite3_result_error(ctx, message, -1)
|
||||
sqlite3_result_error_code(ctx, SQLITE_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -683,46 +683,20 @@ public final class Statement: Equatable, Hashable {
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
/// Binds a string to a parameter in an SQL statement.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - stmt: A pointer to the prepared SQL statement.
|
||||
/// - index: The index of the parameter (1-based).
|
||||
/// - string: The string to be bound to the parameter.
|
||||
/// - Returns: SQLite error code if binding fails.
|
||||
private func sqlite3_bind_text(_ stmt: OpaquePointer!, _ index: Int32, _ string: String) -> Int32 {
|
||||
sqlite3_bind_text(stmt, index, string, -1, SQLITE_TRANSIENT)
|
||||
}
|
||||
|
||||
/// Binds binary data to a parameter in an SQL statement.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - stmt: A pointer to the prepared SQL statement.
|
||||
/// - index: The index of the parameter (1-based).
|
||||
/// - data: The `Data` to be bound to the parameter.
|
||||
/// - Returns: SQLite error code if binding fails.
|
||||
private func sqlite3_bind_blob(_ stmt: OpaquePointer!, _ index: Int32, _ data: Data) -> Int32 {
|
||||
data.withUnsafeBytes {
|
||||
sqlite3_bind_blob(stmt, index, $0.baseAddress, Int32($0.count), SQLITE_TRANSIENT)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves text data from a result column of an SQL statement.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - stmt: A pointer to the prepared SQL statement.
|
||||
/// - iCol: The column index.
|
||||
/// - Returns: A `String` containing the text data from the specified column.
|
||||
private func sqlite3_column_text(_ stmt: OpaquePointer!, _ iCol: Int32) -> String {
|
||||
String(cString: DataLiteC.sqlite3_column_text(stmt, iCol))
|
||||
}
|
||||
|
||||
/// Retrieves binary data from a result column of an SQL statement.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - stmt: A pointer to the prepared SQL statement.
|
||||
/// - iCol: The column index.
|
||||
/// - Returns: A `Data` object containing the binary data from the specified column.
|
||||
private func sqlite3_column_blob(_ stmt: OpaquePointer!, _ iCol: Int32) -> Data {
|
||||
Data(
|
||||
bytes: sqlite3_column_blob(stmt, iCol),
|
||||
|
||||
@@ -242,11 +242,6 @@ on an attached database. If omitted, they apply to the main database.
|
||||
- ``init(location:options:)``
|
||||
- ``init(path:options:)``
|
||||
|
||||
### Delegation
|
||||
|
||||
- ``ConnectionDelegate``
|
||||
- ``delegate``
|
||||
|
||||
### Connection State
|
||||
|
||||
- ``isAutocommit``
|
||||
@@ -261,6 +256,11 @@ on an attached database. If omitted, they apply to the main database.
|
||||
- ``synchronous``
|
||||
- ``userVersion``
|
||||
|
||||
### Delegation
|
||||
|
||||
- ``addDelegate(_:)``
|
||||
- ``removeDelegate(_:)``
|
||||
|
||||
### SQLite Lifecycle
|
||||
|
||||
- ``initialize()``
|
||||
|
||||
@@ -15,11 +15,6 @@ import DataLiteC
|
||||
///
|
||||
/// ## Topics
|
||||
///
|
||||
/// ### Delegation
|
||||
///
|
||||
/// - ``ConnectionDelegate``
|
||||
/// - ``delegate``
|
||||
///
|
||||
/// ### Connection State
|
||||
///
|
||||
/// - ``isAutocommit``
|
||||
@@ -34,6 +29,11 @@ import DataLiteC
|
||||
/// - ``synchronous``
|
||||
/// - ``userVersion``
|
||||
///
|
||||
/// ### Delegation
|
||||
///
|
||||
/// - ``addDelegate(_:)``
|
||||
/// - ``removeDelegate(_:)``
|
||||
///
|
||||
/// ### SQLite Lifecycle
|
||||
///
|
||||
/// - ``initialize()``
|
||||
@@ -70,15 +70,6 @@ import DataLiteC
|
||||
/// - ``apply(_:name:)``
|
||||
/// - ``rekey(_:name:)``
|
||||
public protocol ConnectionProtocol: AnyObject {
|
||||
// MARK: - Delegation
|
||||
|
||||
/// An optional delegate to receive connection-related events and callbacks.
|
||||
///
|
||||
/// The delegate allows external objects to monitor or respond to events
|
||||
/// occurring during the lifetime of the connection, such as errors,
|
||||
/// transaction commits, or other significant state changes.
|
||||
var delegate: ConnectionDelegate? { get set }
|
||||
|
||||
// MARK: - Connection State
|
||||
|
||||
/// Indicates whether the database connection is in autocommit mode.
|
||||
@@ -162,6 +153,18 @@ public protocol ConnectionProtocol: AnyObject {
|
||||
/// - SeeAlso: [PRAGMA user_version](https://www.sqlite.org/pragma.html#pragma_user_version)
|
||||
var userVersion: Int32 { get set }
|
||||
|
||||
// MARK: - Delegation
|
||||
|
||||
/// Adds a delegate to receive connection events.
|
||||
///
|
||||
/// - Parameter delegate: The delegate to add.
|
||||
func addDelegate(_ delegate: ConnectionDelegate)
|
||||
|
||||
/// Removes a delegate from receiving connection events.
|
||||
///
|
||||
/// - Parameter delegate: The delegate to remove.
|
||||
func removeDelegate(_ delegate: ConnectionDelegate)
|
||||
|
||||
// MARK: - SQLite Lifecycle
|
||||
|
||||
/// Initializes the SQLite library.
|
||||
|
||||
Reference in New Issue
Block a user