@@ -129,13 +129,28 @@ private struct TDSHandle: @unchecked Sendable {
129129
130130public actor SQLClient {
131131 public static let shared = SQLClient ( )
132- public init ( ) { }
132+
133+ private static let initializeFreeTDS : Void = {
134+ dbinit ( )
135+ dberrhandle ( SQLClient_errorHandler)
136+ dbmsghandle ( SQLClient_messageHandler)
137+ } ( )
138+
139+ public init ( ) {
140+ _ = SQLClient . initializeFreeTDS
141+ }
133142
134143 private let queue = DispatchQueue ( label: " com.sqlclient.serial " )
135144 private var activeTask : Task < Void , Never > ?
136145
137- private func awaitPrevious( ) async {
138- _ = await activeTask? . result
146+ private func serialize< T: Sendable > ( _ operation: @escaping @Sendable ( ) async throws -> T ) async throws -> T {
147+ let previousTask = activeTask
148+ let newTask : Task < T , Error > = Task {
149+ _ = await previousTask? . result
150+ return try await operation ( )
151+ }
152+ activeTask = Task { _ = await newTask. result }
153+ return try await newTask. value
139154 }
140155
141156 public var maxTextSize : Int = 4096
@@ -148,70 +163,44 @@ public actor SQLClient {
148163 }
149164
150165 public func connect( options: SQLClientConnectionOptions ) async throws {
151- print ( " DEBUG SQL: connect start " )
152- await awaitPrevious ( )
153- print ( " DEBUG SQL: connect entering task " )
154- let task = Task {
155- print ( " DEBUG SQL: connect execution start " )
166+ try await serialize {
156167 guard !self . connected else { throw SQLClientError . alreadyConnected }
157168
158169 let result = try await self . runBlocking {
159- print ( " DEBUG SQL: connect blocking start " )
160170 return try self . _connectSync ( options: options)
161171 }
162172
163173 self . login = result. login. pointer
164174 self . connection = result. connection. pointer
165175 self . connected = true
166- print ( " DEBUG SQL: connect execution complete " )
167176 }
168- activeTask = Task { _ = await task. result }
169- try await task. value
170177 }
171178
172179 public func disconnect( ) async {
173- print ( " DEBUG SQL: disconnect start " )
174- await awaitPrevious ( )
175- print ( " DEBUG SQL: disconnect entering task " )
176- let task = Task {
177- print ( " DEBUG SQL: disconnect execution start " )
180+ _ = try ? await serialize {
178181 guard self . connected else { return }
179182 let lgn = self . login. map { TDSHandle ( pointer: $0) }
180183 let conn = self . connection. map { TDSHandle ( pointer: $0) }
181184 await self . runBlockingVoid {
182- print ( " DEBUG SQL: disconnect blocking start " )
183185 self . _disconnectSync ( login: lgn, connection: conn)
184186 }
185187 self . login = nil
186188 self . connection = nil
187189 self . connected = false
188- print ( " DEBUG SQL: disconnect execution complete " )
189190 }
190- activeTask = Task { _ = await task. result }
191- _ = await task. result
192191 }
193192
194193 public func execute( _ sql: String ) async throws -> SQLClientResult {
195- let snippet = String ( sql. prefix ( 30 ) ) . replacingOccurrences ( of: " \n " , with: " " )
196- print ( " DEBUG SQL: execute start [ \( snippet) ...] " )
197- await awaitPrevious ( )
198- print ( " DEBUG SQL: execute entering task [ \( snippet) ...] " )
199- let task = Task {
200- print ( " DEBUG SQL: execute execution start [ \( snippet) ...] " )
194+ try await serialize {
201195 guard self . connected, let conn = self . connection else { throw SQLClientError . notConnected }
202196 guard !sql. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty else { throw SQLClientError . noCommandText }
203197 let maxText = self . maxTextSize
204198 let handle = TDSHandle ( pointer: conn)
205199
206- let res = try await self . runBlocking {
207- print ( " DEBUG SQL: execute blocking start [ \( snippet) ...] " )
200+ return try await self . runBlocking {
208201 return try self . _executeSync ( sql: sql, connection: handle, maxTextSize: maxText)
209202 }
210- print ( " DEBUG SQL: execute execution complete [ \( snippet) ...] " )
211- return res
212203 }
213- activeTask = Task { _ = await task. result }
214- return try await task. value
215204 }
216205
217206 public func query( _ sql: String ) async throws -> [ SQLRow ] { try await execute ( sql) . rows }
@@ -265,8 +254,8 @@ public actor SQLClient {
265254 }
266255 Thread . sleep ( forTimeInterval: 0.1 )
267256 }
268- CFReadStreamOpen ( read)
269- CFWriteStreamOpen ( write)
257+ CFReadStreamClose ( read)
258+ CFWriteStreamClose ( write)
270259
271260 if connected {
272261 cont. resume ( )
@@ -278,15 +267,14 @@ public actor SQLClient {
278267}
279268
280269 private nonisolated func _connectSync( options: SQLClientConnectionOptions ) throws -> ( login: TDSHandle , connection: TDSHandle ) {
281- dbinit ( )
282- dberrhandle ( SQLClient_errorHandler)
283- dbmsghandle ( SQLClient_messageHandler)
284-
285270 guard let lgn = dblogin ( ) else { throw SQLClientError . loginAllocationFailed }
286271
287272 dbsetlname ( lgn, options. username, 2 ) // DBSETUSER
288273 dbsetlname ( lgn, options. password, 3 ) // DBSETPWD
289274 dbsetlname ( lgn, " SQLClientSwift " , 5 ) // DBSETAPP
275+
276+ // Ensure we get UTF-8 from the server for N-types
277+ dbsetlcharset ( lgn, " UTF-8 " )
290278
291279 if let port = options. port { dbsetlshort ( lgn, Int32 ( port) , 13 ) } // DBSETPORT
292280 if options. encryption != . request { dbsetlname ( lgn, options. encryption. rawValue, 17 ) } // DBSETENCRYPTION
@@ -320,6 +308,10 @@ public actor SQLClient {
320308
321309 private nonisolated func _executeSync( sql: String , connection: TDSHandle , maxTextSize: Int ) throws -> SQLClientResult {
322310 let conn = connection. pointer
311+
312+ // Ensure any previous results are cancelled before a new command
313+ dbcancel ( conn)
314+
323315 _ = dbsetopt ( conn, DBTEXTSIZE, " \( maxTextSize) " , - 1 )
324316
325317 guard dbcmd ( conn, sql) != FAIL, dbsqlexec ( conn) != FAIL else { throw SQLClientError . executionFailed }
@@ -382,7 +374,12 @@ public actor SQLClient {
382374 return NSNumber ( value: data. load ( as: UInt8 . self) != 0 )
383375 case 47 , 39 , 102 , 103 , 35 , 99 , 241 : // SYBCHAR, SYBVARCHAR, SYBTEXT, SYBNTEXT, SYBXML, SYBNCHAR, SYBNVARCHAR
384376 let buf = UnsafeBufferPointer < UInt8 > ( start: data. assumingMemoryBound ( to: UInt8 . self) , count: Int ( len) )
385- return String ( bytes: buf, encoding: . utf8) ?? String ( bytes: buf, encoding: . windowsCP1252) ?? " "
377+ // Try UTF-8 first, then windowsCP1252 as fallback
378+ if let str = String ( bytes: buf, encoding: . utf8) { return str }
379+ if let str = String ( bytes: buf, encoding: . windowsCP1252) { return str }
380+ // If it's UCS-2 (type 103/SYBNVARCHAR usually), try UTF-16
381+ if let str = String ( bytes: buf, encoding: . utf16LittleEndian) { return str }
382+ return " "
386383 case 45 , 37 , 34 , 173 , 174 , 167 : // SYBBINARY, SYBVARBINARY, SYBIMAGE, SYBBIGBINARY, SYBBIGVARBINARY, SYBBLOB
387384 return Data ( bytes: dataPtr, count: Int ( len) )
388385 case 61 , 58 , 111 : // SYBDATETIME, SYBDATETIME4, SYBDATETIMN
0 commit comments