Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Copy as INSERT/UPDATE SQL statements from data grid context menu
- Plugin download count display in Browse Plugins — fetched from GitHub Releases API and cached for 1 hour
- MSSQL query cancellation (`cancelQuery`) and lock timeout (`applyQueryTimeout`) support

### Fixed

Expand Down
33 changes: 33 additions & 0 deletions Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ private final class FreeTDSConnection: @unchecked Sendable {
private let database: String
private let lock = NSLock()
private var _isConnected = false
private var _isCancelled = false

var isConnected: Bool {
lock.lock()
Expand Down Expand Up @@ -172,6 +173,16 @@ private final class FreeTDSConnection: @unchecked Sendable {
}
}

func cancelCurrentQuery() {
lock.lock()
_isCancelled = true
let proc = dbproc
lock.unlock()

guard let proc else { return }
dbcancel(proc)
}
Comment thread
datlechin marked this conversation as resolved.

func executeQuery(_ query: String) async throws -> FreeTDSQueryResult {
let queryToRun = String(query)
return try await pluginDispatchAsync(on: queue) { [self] in
Expand All @@ -186,6 +197,10 @@ private final class FreeTDSConnection: @unchecked Sendable {

_ = dbcanquery(proc)

lock.lock()
_isCancelled = false
lock.unlock()

freetdsLastError = ""
if dbcmd(proc, query) == FAIL {
throw MSSQLPluginError.queryFailed("Failed to prepare query")
Expand Down Expand Up @@ -232,6 +247,14 @@ private final class FreeTDSConnection: @unchecked Sendable {
if rowCode == Int32(NO_MORE_ROWS) { break }
if rowCode == FAIL { break }

lock.lock()
let cancelled = _isCancelled
if cancelled { _isCancelled = false }
lock.unlock()
if cancelled {
throw MSSQLPluginError.queryFailed("Query cancelled")
}

var row: [String?] = []
for i in 1...numCols {
let len = dbdatlen(proc, Int32(i))
Expand Down Expand Up @@ -386,6 +409,16 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
)
}

func cancelQuery() throws {
freeTDSConn?.cancelCurrentQuery()
}

func applyQueryTimeout(_ seconds: Int) async throws {
guard seconds > 0 else { return }
let ms = seconds * 1_000
_ = try await execute(query: "SET LOCK_TIMEOUT \(ms)")
}
Comment thread
datlechin marked this conversation as resolved.

func executeParameterized(query: String, parameters: [String?]) async throws -> PluginQueryResult {
guard !parameters.isEmpty else {
return try await execute(query: query)
Expand Down
Loading