Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The Inspector toolbar button was permanently dimmed on macOS 13.
- A connection's status on the welcome window stopped updating once the window was open.
- The Compare & Sync licence notice froze the app instead of opening as a sheet.
- `VACUUM`, `CREATE INDEX CONCURRENTLY`, `SET sql_log_bin` and `PRAGMA foreign_keys` failing or ignored when running several statements.
- A batch committing, discarding or aborting a transaction already open on the connection.
- Grid saves, structure changes and a Users & Roles apply committing a transaction already open on the connection.
- Stopping a multi-statement run committing the batch anyway, or leaving its transaction open on the connection.
- A lost connection during a commit reported as a clean rollback.
- Query in one tab cancelled and rolled back when another tab or window on the connection starts or stops a query.
- Stop on MySQL 5.5, 5.6 or MariaDB 5.5 interrupting the next statement on the connection.
- `QUEUED` results and hidden command errors when running several Redis commands or saving Redis grid edits.
- Query timeout ignored on MySQL before 5.7.8 and MariaDB before 10.1.1.
- MySQL query run a second time, and left running on the server, after the connection timed out.
- Empty error when a parameterized MySQL query timed out.
- Check constraints reported as added on MySQL before 8.0.16 and MariaDB before 10.2.1, which discard them.
- `Unknown table 'CHECK_CONSTRAINTS'` opening a table's structure on MariaDB 10.2 before 10.2.22 and 10.3 before 10.3.10.
- Syntax error removing a check constraint on MySQL 8.0.16 to 8.0.18.
- Syntax error setting a password or connection limit in Users & Roles on MySQL before 5.7.6 and MariaDB before 10.2.
- Passwordless account created when a new user's connection limit was set before saving.
- Connection held as "session settings changed" after a `SET PASSWORD`.
- Rows saved, added or deleted on iPhone and iPad refused by a MySQL, MariaDB, PostgreSQL or Redshift server that starts sessions read-only.
- Copying objects into a connection that already has a transaction open committing it.
- Replace-copy into a remote libSQL target failing at `BEGIN`.

### Security

Expand Down
4 changes: 2 additions & 2 deletions Packages/TableProCore/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ let package = Package(
),
.target(
name: "TableProDatabase",
dependencies: ["TableProModels", "TableProCoreTypes"],
dependencies: ["TableProModels", "TableProCoreTypes", "TableProPluginKit"],
path: "Sources/TableProDatabase"
),
.target(
Expand Down Expand Up @@ -163,7 +163,7 @@ let package = Package(
),
.testTarget(
name: "TableProDatabaseTests",
dependencies: ["TableProDatabase", "TableProModels"],
dependencies: ["TableProDatabase", "TableProModels", "TableProPluginKit"],
path: "Tests/TableProDatabaseTests"
),
.testTarget(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Foundation
import TableProPluginKit

public extension DatabaseDriver {
@discardableResult
func executeWrite(_ statements: [String]) async throws -> Int {
guard !statements.isEmpty else { return 0 }

let opensTransaction = await WriteTransactionPolicy.opensTransaction(
supportsTransactions: supportsTransactions,
state: sessionTransactionState(),
statementCount: statements.count
)
guard opensTransaction else { return try await runWriteStatements(statements) }

try await beginTransaction(mode: .readWrite)
do {
let affected = try await runWriteStatements(statements)
try await commitTransaction()
return affected
} catch {
try? await rollbackTransaction()
throw error
}
}
}

private extension DatabaseDriver {
func runWriteStatements(_ statements: [String]) async throws -> Int {
var affected = 0
for statement in statements {
let result = try await execute(query: statement)
affected += max(result.rowsAffected, 0)
}
return affected
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import TableProModels
import TableProPluginKit

public protocol DatabaseDriver: AnyObject, Sendable {
func connect() async throws
Expand All @@ -25,8 +26,10 @@ public protocol DatabaseDriver: AnyObject, Sendable {

var supportsTransactions: Bool { get }
func beginTransaction() async throws
func beginTransaction(mode: PluginTransactionAccessMode) async throws
func commitTransaction() async throws
func rollbackTransaction() async throws
func sessionTransactionState() async -> DriverTransactionState

var serverVersion: String? { get }

Expand All @@ -36,6 +39,12 @@ public protocol DatabaseDriver: AnyObject, Sendable {
public extension DatabaseDriver {
var holdsSuspensionBlockingResource: Bool { false }

func beginTransaction(mode: PluginTransactionAccessMode) async throws {
try await beginTransaction()
}

func sessionTransactionState() async -> DriverTransactionState { .unknown }

func escapeStringLiteral(_ value: String) -> String {
SQLEscaping.ansiStringLiteral(value)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

public enum DriverTransactionState: Sendable, Equatable {
case idle
case explicitTransaction
case implicitTransaction
case unknown
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation

public enum WriteTransactionPolicy {
public static func opensTransaction(
supportsTransactions: Bool,
state: DriverTransactionState,
statementCount: Int
) -> Bool {
guard supportsTransactions else { return false }
switch state {
case .idle, .implicitTransaction:
return true
case .explicitTransaction:
return false
case .unknown:
return statementCount > 1
}
}
}
Loading
Loading