From e48c37aca4face002b6a7ae00d7dd2312396ff0a Mon Sep 17 00:00:00 2001 From: Oleksii Zghurskyi Date: Tue, 11 Nov 2025 16:28:51 +0200 Subject: [PATCH] Add changes & totalChanges properties for connection --- Sources/DataLiteCore/Classes/Connection.swift | 8 +++ .../Protocols/ConnectionProtocol.swift | 10 ++++ .../Classes/ConnectionTests.swift | 57 +++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/Sources/DataLiteCore/Classes/Connection.swift b/Sources/DataLiteCore/Classes/Connection.swift index d08389a..85000b6 100644 --- a/Sources/DataLiteCore/Classes/Connection.swift +++ b/Sources/DataLiteCore/Classes/Connection.swift @@ -131,6 +131,14 @@ extension Connection: ConnectionProtocol { sqlite3_db_readonly(connection, "main") == 1 } + public var changes: Int64 { + sqlite3_changes64(connection) + } + + public var totalChanges: Int64 { + sqlite3_total_changes64(connection) + } + public static func initialize() throws(SQLiteError) { let status = sqlite3_initialize() guard status == SQLITE_OK else { diff --git a/Sources/DataLiteCore/Protocols/ConnectionProtocol.swift b/Sources/DataLiteCore/Protocols/ConnectionProtocol.swift index 1982b6c..812e9ca 100644 --- a/Sources/DataLiteCore/Protocols/ConnectionProtocol.swift +++ b/Sources/DataLiteCore/Protocols/ConnectionProtocol.swift @@ -12,6 +12,8 @@ import Foundation /// /// - ``isAutocommit`` /// - ``isReadonly`` +/// - ``changes`` +/// - ``totalChanges`` /// /// ### Accessing PRAGMA Values /// @@ -84,6 +86,14 @@ public protocol ConnectionProtocol: AnyObject { /// - SeeAlso: [Determine if a database is read-only](https://sqlite.org/c3ref/db_readonly.html) var isReadonly: Bool { get } + /// The number of rows modified by the most recent write operation. + /// - SeeAlso: [Count The Number Of Rows Modified](https://sqlite.org/c3ref/changes.html) + var changes: Int64 { get } + + /// The total number of rows modified since this connection was opened. + /// - SeeAlso: [Total Number Of Rows Modified](https://sqlite.org/c3ref/total_changes.html) + var totalChanges: Int64 { get } + // MARK: - PRAGMA Accessors /// The busy timeout of the connection, in milliseconds. diff --git a/Tests/DataLiteCoreTests/Classes/ConnectionTests.swift b/Tests/DataLiteCoreTests/Classes/ConnectionTests.swift index bd78b8f..33cf0b6 100644 --- a/Tests/DataLiteCoreTests/Classes/ConnectionTests.swift +++ b/Tests/DataLiteCoreTests/Classes/ConnectionTests.swift @@ -63,6 +63,63 @@ struct ConnectionTests { #expect(connection.isReadonly == expected) } + @Test func changes() throws { + let connection = try Connection( + location: .inMemory, options: [.create, .readwrite] + ) + + try connection.execute(sql: "CREATE TABLE t (id INT PRIMARY KEY)") + #expect(connection.changes == 0) + + try connection.execute(sql: "INSERT INTO t (id) VALUES (1),(2)") + #expect(connection.changes == 2) + + try connection.execute(sql: "UPDATE t SET id = 3 WHERE id = 1") + #expect(connection.changes == 1) + + try connection.execute(sql: "DELETE FROM t WHERE id = 2") + #expect(connection.changes == 1) + + try connection.execute(sql: "UPDATE t SET id = 3 WHERE id = -1") + #expect(connection.changes == 0) + + try connection.execute(sql: "INSERT INTO t (id) VALUES (4),(5)") + try connection.execute(sql: "SELECT * FROM t") + #expect(connection.changes == 2) + } + + @Test func totalChanges() throws { + let connection = try Connection( + location: .inMemory, options: [.create, .readwrite] + ) + + try connection.execute(sql: "CREATE TABLE t (id INT PRIMARY KEY)") + #expect(connection.totalChanges == 0) + + try connection.execute(sql: "INSERT INTO t (id) VALUES (1),(2)") + #expect(connection.totalChanges == 2) + + try connection.execute(sql: "UPDATE t SET id = 3 WHERE id = 1") + #expect(connection.totalChanges == 3) + + try connection.execute(sql: "DELETE FROM t WHERE id = 2") + #expect(connection.totalChanges == 4) + + // No-op update — does not increase totalChanges + try connection.execute(sql: "UPDATE t SET id = 3 WHERE id = -1") + #expect(connection.totalChanges == 4) + + // Read-only statements do not affect totals + try connection.execute(sql: "SELECT * FROM t") + #expect(connection.totalChanges == 4) + + try connection.execute(sql: "INSERT INTO t (id) VALUES (4),(5)") + #expect(connection.totalChanges == 6) + + try connection.execute(sql: "DELETE FROM t") + #expect(connection.totalChanges == 9) + } + @Test func testBusyTimeout() throws { let connection = try Connection( location: .inMemory, options: [.create, .readwrite]