Add multicast delegate
This commit is contained in:
@@ -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,8 +142,8 @@ private func traceCallback(
|
||||
.fromOpaque(ctx)
|
||||
.takeUnretainedValue()
|
||||
|
||||
if let delegate = connection.delegate {
|
||||
guard let stmt = OpaquePointer(p),
|
||||
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 }
|
||||
@@ -133,7 +151,9 @@ private func traceCallback(
|
||||
let pSqlString = String(cString: pSql)
|
||||
let xSqlString = String(cString: xSql)
|
||||
let trace = (xSqlString, pSqlString)
|
||||
delegate.connection(connection, trace: trace)
|
||||
|
||||
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 {
|
||||
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)
|
||||
|
||||
do {
|
||||
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