Skip to content

Commit c89c6fb

Browse files
committed
Correct serialization logic in SQLClient actor
1 parent 4ecfe50 commit c89c6fb

1 file changed

Lines changed: 36 additions & 21 deletions

File tree

Sources/SQLClientSwift/SQLClient.swift

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ public actor SQLClient {
132132
public init() {}
133133

134134
private let queue = DispatchQueue(label: "com.sqlclient.serial")
135+
private var activeTask: Task<Void, Never>?
136+
137+
private func serialize<T: Sendable>(_ operation: @escaping @Sendable () async throws -> T) async throws -> T {
138+
let newTask: Task<T, Error> = Task { [activeTask] in
139+
_ = await activeTask?.result
140+
return try await operation()
141+
}
142+
activeTask = Task { _ = await newTask.result }
143+
return try await newTask.value
144+
}
135145

136146
public var maxTextSize: Int = 4096
137147
private var login: OpaquePointer?
@@ -143,37 +153,43 @@ public actor SQLClient {
143153
}
144154

145155
public func connect(options: SQLClientConnectionOptions) async throws {
146-
guard !connected else { throw SQLClientError.alreadyConnected }
156+
try await serialize {
157+
guard !self.connected else { throw SQLClientError.alreadyConnected }
147158

148-
let result = try await runBlocking {
159+
let result = try await self.runBlocking {
149160
return try self._connectSync(options: options)
150161
}
151162

152163
self.login = result.login.pointer
153164
self.connection = result.connection.pointer
154165
self.connected = true
155166
}
167+
}
156168

157169
public func disconnect() async {
158-
guard connected else { return }
159-
let lgn = self.login.map { TDSHandle(pointer: $0) }
160-
let conn = self.connection.map { TDSHandle(pointer: $0) }
161-
await runBlockingVoid {
162-
self._disconnectSync(login: lgn, connection: conn)
170+
_ = try? await serialize {
171+
guard self.connected else { return }
172+
let lgn = self.login.map { TDSHandle(pointer: $0) }
173+
let conn = self.connection.map { TDSHandle(pointer: $0) }
174+
await self.runBlockingVoid {
175+
self._disconnectSync(login: lgn, connection: conn)
176+
}
177+
self.login = nil
178+
self.connection = nil
179+
self.connected = false
163180
}
164-
self.login = nil
165-
self.connection = nil
166-
self.connected = false
167181
}
168182

169183
public func execute(_ sql: String) async throws -> SQLClientResult {
170-
guard connected, let conn = connection else { throw SQLClientError.notConnected }
171-
guard !sql.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { throw SQLClientError.noCommandText }
172-
let maxText = self.maxTextSize
173-
let handle = TDSHandle(pointer: conn)
174-
175-
return try await runBlocking {
176-
return try self._executeSync(sql: sql, connection: handle, maxTextSize: maxText)
184+
try await serialize {
185+
guard self.connected, let conn = self.connection else { throw SQLClientError.notConnected }
186+
guard !sql.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { throw SQLClientError.noCommandText }
187+
let maxText = self.maxTextSize
188+
let handle = TDSHandle(pointer: conn)
189+
190+
return try await self.runBlocking {
191+
return try self._executeSync(sql: sql, connection: handle, maxTextSize: maxText)
192+
}
177193
}
178194
}
179195

@@ -228,8 +244,8 @@ public actor SQLClient {
228244
}
229245
Thread.sleep(forTimeInterval: 0.1)
230246
}
231-
CFReadStreamClose(read)
232-
CFWriteStreamClose(write)
247+
CFReadStreamOpen(read)
248+
CFWriteStreamOpen(write)
233249

234250
if connected {
235251
cont.resume()
@@ -343,7 +359,7 @@ public actor SQLClient {
343359
return NSNumber(value: data.load(as: Double.self))
344360
case 50, 104: // SYBBIT, SYBBITN
345361
return NSNumber(value: data.load(as: UInt8.self) != 0)
346-
case 47, 39, 102, 103, 35, 99, 241: // SYBCHAR, SYBVARCHAR, SYBNCHAR, SYBNVARCHAR, SYBTEXT, SYBNTEXT, SYBXML
362+
case 47, 39, 102, 103, 35, 99, 241: // SYBCHAR, SYBVARCHAR, SYBTEXT, SYBNTEXT, SYBXML, SYBNCHAR, SYBNVARCHAR
347363
let buf = UnsafeBufferPointer<UInt8>(start: data.assumingMemoryBound(to: UInt8.self), count: Int(len))
348364
return String(bytes: buf, encoding: .utf8) ?? String(bytes: buf, encoding: .windowsCP1252) ?? ""
349365
case 45, 37, 34, 173, 174, 167: // SYBBINARY, SYBVARBINARY, SYBIMAGE, SYBBIGBINARY, SYBBIGVARBINARY, SYBBLOB
@@ -360,7 +376,6 @@ public actor SQLClient {
360376
memcpy(&bytes, dataPtr, 16)
361377

362378
// SQL Server UniqueIdentifier mixed-endian -> RFC 4122 Big-Endian
363-
// Data1 (4 bytes), Data2 (2 bytes), Data3 (2 bytes) are little-endian in TDS
364379
let swapped: [UInt8] = [
365380
bytes[3], bytes[2], bytes[1], bytes[0],
366381
bytes[5], bytes[4],

0 commit comments

Comments
 (0)