@@ -40,20 +40,31 @@ final class SnowflakeConnection: @unchecked Sendable {
4040
4141 let host : String
4242 let params : ResolvedParameters
43+ private let connectionIdentifier : String
4344
4445 private let session : URLSession
4546 private let lock = NSLock ( )
4647 private let heartbeat = SnowflakeHeartbeat ( )
4748 private var sessionToken : String ?
4849 private var renewalToken : String ?
49- private var activeRequestIDs : Set < String > = [ ]
50+ private var activeRequestIDs : [ String : Set < String > ] = [ : ]
5051 private var sequenceId = 0
5152 private var connectTask : Task < Void , Error > ?
5253
5354 var sessionFingerprint : String {
54- [ host, params. user. uppercased ( ) , params. authMethod, params. role. uppercased ( ) ] . joined ( separator: " | " )
55+ SnowflakeSessionKey . fingerprint (
56+ connectionId: connectionIdentifier,
57+ host: host,
58+ user: params. user,
59+ authMethod: params. authMethod,
60+ role: params. role
61+ )
5562 }
5663
64+ /// Statements the connection issues for itself, such as the connect-time `USE` calls, belong to
65+ /// no driver and are never the target of a Stop.
66+ static let sessionOwner = " session "
67+
5768 private var _currentDatabase : String ?
5869 private var _currentSchema : String ?
5970 private var _currentWarehouse : String ?
@@ -71,6 +82,7 @@ final class SnowflakeConnection: @unchecked Sendable {
7182 init ( config: DriverConnectionConfig ) {
7283 self . params = Self . resolveParameters ( from: config)
7384 self . host = SnowflakeAccount . host ( forAccount: params. account)
85+ self . connectionIdentifier = config. additionalFields [ " connectionId " ] ?? " "
7486
7587 let configuration = URLSessionConfiguration . ephemeral
7688 configuration. timeoutIntervalForRequest = 120
@@ -419,9 +431,13 @@ final class SnowflakeConnection: @unchecked Sendable {
419431
420432 // MARK: - Query Execution
421433
422- func query( _ sql: String , parameters: [ PluginCellValue ] = [ ] ) async throws -> SnowflakeQueryResult {
434+ func query(
435+ _ sql: String ,
436+ parameters: [ PluginCellValue ] = [ ] ,
437+ owner: String = SnowflakeConnection . sessionOwner
438+ ) async throws -> SnowflakeQueryResult {
423439 try await withReauthentication {
424- try await performQuery ( sql, parameters: parameters)
440+ try await performQuery ( sql, parameters: parameters, owner : owner )
425441 }
426442 }
427443
@@ -439,8 +455,12 @@ final class SnowflakeConnection: @unchecked Sendable {
439455 }
440456 }
441457
442- func cancelAllQueries( ) {
443- let ( requestIDs, token) = lock. withLock { ( activeRequestIDs, sessionToken) }
458+ /// One Snowflake session is shared by the driver the user sees and by every pooled metadata
459+ /// driver behind it, so an abort has to name whose work it is stopping. Aborting the whole
460+ /// session made Stop on a query cancel a sidebar refresh running beside it, and cancel a save's
461+ /// remaining statements.
462+ func cancelQueries( owner: String ) {
463+ let ( requestIDs, token) = lock. withLock { ( activeRequestIDs [ owner] ?? [ ] , sessionToken) }
444464 guard !requestIDs. isEmpty, let token else { return }
445465 Task { [ weak self] in
446466 for requestID in requestIDs {
@@ -454,8 +474,12 @@ final class SnowflakeConnection: @unchecked Sendable {
454474 }
455475 }
456476
457- private func performQuery( _ sql: String , parameters: [ PluginCellValue ] = [ ] ) async throws -> SnowflakeQueryResult {
458- let ( data, token) = try await submitQuery ( sql, parameters: parameters)
477+ private func performQuery(
478+ _ sql: String ,
479+ parameters: [ PluginCellValue ] = [ ] ,
480+ owner: String
481+ ) async throws -> SnowflakeQueryResult {
482+ let ( data, token) = try await submitQuery ( sql, parameters: parameters, owner: owner)
459483 if let resultIds = data [ " resultIds " ] as? String , !resultIds. isEmpty {
460484 return try await collectMultiStatementResults ( ids: resultIds, token: token)
461485 }
@@ -465,7 +489,8 @@ final class SnowflakeConnection: @unchecked Sendable {
465489
466490 private func submitQuery(
467491 _ sql: String ,
468- parameters: [ PluginCellValue ]
492+ parameters: [ PluginCellValue ] ,
493+ owner: String
469494 ) async throws -> ( data: [ String : Any ] , token: String ) {
470495 guard let token = lock. withLock ( { sessionToken } ) else {
471496 throw SnowflakeError . notConnected
@@ -474,11 +499,14 @@ final class SnowflakeConnection: @unchecked Sendable {
474499 let requestID = UUID ( ) . uuidString. lowercased ( )
475500 let sequence = lock. withLock { ( ) -> Int in
476501 sequenceId += 1
477- activeRequestIDs. insert ( requestID)
502+ activeRequestIDs [ owner , default : [ ] ] . insert ( requestID)
478503 return sequenceId
479504 }
480505 defer {
481- lock. withLock { _ = activeRequestIDs. remove ( requestID) }
506+ lock. withLock {
507+ activeRequestIDs [ owner] ? . remove ( requestID)
508+ if activeRequestIDs [ owner] ? . isEmpty == true { activeRequestIDs [ owner] = nil }
509+ }
482510 }
483511
484512 var body : [ String : Any ] = [
@@ -611,9 +639,12 @@ final class SnowflakeConnection: @unchecked Sendable {
611639 let batches : AsyncThrowingStream < [ [ PluginCellValueBox ] ] , Error >
612640 }
613641
614- func queryStreamed( _ sql: String ) async throws -> StreamedResult {
642+ func queryStreamed(
643+ _ sql: String ,
644+ owner: String = SnowflakeConnection . sessionOwner
645+ ) async throws -> StreamedResult {
615646 let ( data, _) = try await withReauthentication {
616- try await submitQuery ( sql, parameters: [ ] )
647+ try await submitQuery ( sql, parameters: [ ] , owner : owner )
617648 }
618649 applyFinalSessionInfo ( data)
619650
0 commit comments