@@ -38,6 +38,12 @@ final class CassandraPlugin: NSObject, TableProPlugin, DriverPlugin {
3838private actor CassandraConnectionActor {
3939 private static let logger = Logger ( subsystem: " com.TablePro.CassandraDriver " , category: " Connection " )
4040
41+ nonisolated ( unsafe) private static let isoFormatter : ISO8601DateFormatter = {
42+ let f = ISO8601DateFormatter ( )
43+ f. formatOptions = [ . withInternetDateTime, . withFractionalSeconds]
44+ return f
45+ } ( )
46+
4147 private var cluster : OpaquePointer ? // CassCluster*
4248 private var session : OpaquePointer ? // CassSession*
4349 private var currentKeyspace : String ?
@@ -69,15 +75,23 @@ private actor CassandraConnectionActor {
6975
7076 // SSL/TLS
7177 if sslMode != " Disabled " {
72- let ssl = cass_ssl_new ( )
78+ guard let ssl = cass_ssl_new ( ) else {
79+ cass_cluster_free ( cluster)
80+ self . cluster = nil
81+ throw CassandraPluginError . connectionFailed ( " Failed to create SSL context " )
82+ }
7383
7484 if sslMode == " Verify CA " || sslMode == " Verify Identity " {
7585 cass_ssl_set_verify_flags ( ssl, Int32 ( CASS_SSL_VERIFY_PEER_CERT . rawValue) )
7686
7787 if let caCertPath = sslCaCertPath, !caCertPath. isEmpty,
7888 let certData = FileManager . default. contents ( atPath: caCertPath) ,
7989 let certString = String ( data: certData, encoding: . utf8) {
80- cass_ssl_add_trusted_cert ( ssl, certString)
90+ let rc = cass_ssl_add_trusted_cert ( ssl, certString)
91+ if rc != CASS_OK {
92+ Self . logger. warning ( " Failed to add CA certificate, proceeding without verification " )
93+ cass_ssl_set_verify_flags ( ssl, Int32 ( CASS_SSL_VERIFY_NONE . rawValue) )
94+ }
8195 }
8296 } else {
8397 // "Preferred" / "Required" — encrypt but skip cert verification
@@ -437,9 +451,7 @@ private actor CassandraConnectionActor {
437451 var timestamp : Int64 = 0
438452 if cass_value_get_int64 ( value, & timestamp) == CASS_OK {
439453 let date = Date ( timeIntervalSince1970: Double ( timestamp) / 1000.0 )
440- let formatter = ISO8601DateFormatter ( )
441- formatter. formatOptions = [ . withInternetDateTime, . withFractionalSeconds]
442- return formatter. string ( from: date)
454+ return isoFormatter. string ( from: date)
443455 }
444456 return nil
445457
@@ -650,7 +662,12 @@ final class CassandraPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
650662
651663 func disconnect( ) {
652664 let actor = connectionActor
653- Task { await actor . close ( ) }
665+ let semaphore = DispatchSemaphore ( value: 0 )
666+ Task {
667+ await actor . close ( )
668+ semaphore. signal ( )
669+ }
670+ semaphore. wait ( )
654671 stateLock. lock ( )
655672 _currentKeyspace = nil
656673 _cachedVersion = nil
@@ -696,15 +713,17 @@ final class CassandraPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
696713 // MARK: - Pagination
697714
698715 func fetchRowCount( query: String ) async throws -> Int {
699- let countQuery = " SELECT COUNT(*) FROM ( \( stripTrailingSemicolon ( query) ) ) "
700- let result = try await execute ( query: countQuery)
701- guard let firstRow = result. rows. first, let countStr = firstRow. first else { return 0 }
702- return Int ( countStr ?? " 0 " ) ?? 0
716+ // CQL does not support subqueries, so we can't wrap an arbitrary query in SELECT COUNT(*) FROM (...).
717+ // Return -1 to signal unknown count; the UI will hide the total page count.
718+ - 1
703719 }
704720
705721 func fetchRows( query: String , offset: Int , limit: Int ) async throws -> PluginQueryResult {
706- // Cassandra doesn't support OFFSET natively.
707- // For paginated browsing, we use LIMIT and let the default protocol handle paging.
722+ // CQL does not support OFFSET. Only the first page (offset=0) can be fetched via simple LIMIT.
723+ // For offset>0, throw so the caller knows pagination is unsupported for arbitrary queries.
724+ if offset > 0 {
725+ throw CassandraPluginError . unsupportedOperation
726+ }
708727 let baseQuery = stripTrailingSemicolon ( query)
709728 let paginatedQuery = " \( baseQuery) LIMIT \( limit) "
710729 return try await execute ( query: paginatedQuery)
0 commit comments