From 33803aae3a3c77af966ed4731bc85330dc3fcccc Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 11 Mar 2026 00:35:16 +0700 Subject: [PATCH 1/2] fix: anchor SQL preview popover to eye button in toolbar --- TablePro/Views/Toolbar/TableProToolbarView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TablePro/Views/Toolbar/TableProToolbarView.swift b/TablePro/Views/Toolbar/TableProToolbarView.swift index 3fda55aa..2e8a093f 100644 --- a/TablePro/Views/Toolbar/TableProToolbarView.swift +++ b/TablePro/Views/Toolbar/TableProToolbarView.swift @@ -139,6 +139,9 @@ struct TableProToolbar: ViewModifier { : state.databaseType == .redis ? "Preview Commands (⌘⇧P)" : "Preview SQL (⌘⇧P)") .disabled(!state.hasPendingChanges || state.connectionState != .connected) + .popover(isPresented: $state.showSQLReviewPopover) { + SQLReviewPopover(statements: state.previewStatements, databaseType: state.databaseType) + } } ToolbarItem(placement: .primaryAction) { @@ -179,9 +182,6 @@ struct TableProToolbar: ViewModifier { } } } - .popover(isPresented: $state.showSQLReviewPopover) { - SQLReviewPopover(statements: state.previewStatements, databaseType: state.databaseType) - } .onReceive(NotificationCenter.default.publisher(for: .openConnectionSwitcher)) { _ in showConnectionSwitcher = true } From 6ca6395426ad9b8cd3fc060cef7a29319e7453e9 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 11 Mar 2026 00:40:31 +0700 Subject: [PATCH 2/2] fix: address review issues from plugin standardization PR --- .../MongoDBConnection.swift | 16 ++++++------ .../MongoDBPluginDriver.swift | 25 +++++++++---------- .../RedisCommandParser.swift | 4 +-- .../PluginConcurrencySupport.swift | 2 +- .../TableProPluginKit/PluginDriverError.swift | 4 +-- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift b/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift index 0ea009c8..dccf1988 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift @@ -369,7 +369,7 @@ final class MongoDBConnection: @unchecked Sendable { projection: String? = nil, skip: Int, limit: Int - ) async throws -> [[String: Any]] { + ) async throws -> (docs: [[String: Any]], isTruncated: Bool) { #if canImport(CLibMongoc) resetCancellation() return try await pluginDispatchAsync(on: queue) { [self] in @@ -387,7 +387,7 @@ final class MongoDBConnection: @unchecked Sendable { #endif } - func aggregate(database: String, collection: String, pipeline: String) async throws -> [[String: Any]] { + func aggregate(database: String, collection: String, pipeline: String) async throws -> (docs: [[String: Any]], isTruncated: Bool) { #if canImport(CLibMongoc) resetCancellation() return try await pluginDispatchAsync(on: queue) { [self] in @@ -595,7 +595,7 @@ private extension MongoDBConnection { func findSync( client: OpaquePointer, database: String, collection: String, filter: String, sort: String?, projection: String?, skip: Int, limit: Int - ) throws -> [[String: Any]] { + ) throws -> (docs: [[String: Any]], isTruncated: Bool) { try checkCancelled() guard let filterBson = jsonToBson(filter) else { @@ -640,7 +640,7 @@ private extension MongoDBConnection { func aggregateSync( client: OpaquePointer, database: String, collection: String, pipeline: String - ) throws -> [[String: Any]] { + ) throws -> (docs: [[String: Any]], isTruncated: Bool) { try checkCancelled() guard let pipelineBson = jsonToBson(pipeline) else { @@ -833,14 +833,15 @@ private extension MongoDBConnection { } defer { mongoc_cursor_destroy(cursor) } - return try iterateCursor(cursor) + return try iterateCursor(cursor).docs } - func iterateCursor(_ cursor: OpaquePointer) throws -> [[String: Any]] { + func iterateCursor(_ cursor: OpaquePointer) throws -> (docs: [[String: Any]], isTruncated: Bool) { try checkCancelled() var results: [[String: Any]] = [] var docPtr: OpaquePointer? + var truncated = false while mongoc_cursor_next(cursor, &docPtr) { try checkCancelled() @@ -850,6 +851,7 @@ private extension MongoDBConnection { } if results.count >= PluginRowLimits.defaultMax { + truncated = true logger.warning("Result set truncated at \(PluginRowLimits.defaultMax) documents") break } @@ -859,7 +861,7 @@ private extension MongoDBConnection { if mongoc_cursor_error(cursor, &error) { throw makeError(error) } - return results + return (docs: results, isTruncated: truncated) } } #endif diff --git a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift index 72db06b6..256d65f9 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift @@ -123,8 +123,8 @@ final class MongoDBPluginDriver: PluginDatabaseDriver { case .findOne: return 1 case .aggregate(let collection, let pipeline): - let docs = try await conn.aggregate(database: db, collection: collection, pipeline: pipeline) - return docs.count + let result = try await conn.aggregate(database: db, collection: collection, pipeline: pipeline) + return result.docs.count case .countDocuments(let collection, let filter): let count = try await conn.countDocuments(database: db, collection: collection, filter: filter) return Int(count) @@ -148,12 +148,12 @@ final class MongoDBPluginDriver: PluginDatabaseDriver { case .find(let collection, let filter, var options): options.skip = offset options.limit = limit - let docs = try await conn.find( + let result = try await conn.find( database: db, collection: collection, filter: filter, sort: options.sort, projection: options.projection, skip: offset, limit: limit ) - return buildPluginResult(from: docs, startTime: startTime) + return buildPluginResult(from: result.docs, startTime: startTime, isTruncated: result.isTruncated) default: return try await executeOperation(operation, connection: conn, startTime: startTime) } @@ -178,7 +178,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver { let docs = try await conn.find( database: currentDb, collection: table, filter: "{}", sort: nil, projection: nil, skip: 0, limit: 500 - ) + ).docs if docs.isEmpty { return [ @@ -527,30 +527,29 @@ final class MongoDBPluginDriver: PluginDatabaseDriver { switch operation { case .find(let collection, let filter, let options): - let docs = try await conn.find( + let result = try await conn.find( database: db, collection: collection, filter: filter, sort: options.sort, projection: options.projection, skip: options.skip ?? 0, limit: options.limit ?? PluginRowLimits.defaultMax ) - if docs.isEmpty { + if result.docs.isEmpty { return PluginQueryResult( columns: ["_id"], columnTypeNames: ["ObjectId"], rows: [], rowsAffected: 0, executionTime: Date().timeIntervalSince(startTime) ) } - let truncated = docs.count >= PluginRowLimits.defaultMax - return buildPluginResult(from: docs, startTime: startTime, isTruncated: truncated) + return buildPluginResult(from: result.docs, startTime: startTime, isTruncated: result.isTruncated) case .findOne(let collection, let filter): - let docs = try await conn.find( + let result = try await conn.find( database: db, collection: collection, filter: filter, sort: nil, projection: nil, skip: 0, limit: 1 ) - return buildPluginResult(from: docs, startTime: startTime) + return buildPluginResult(from: result.docs, startTime: startTime) case .aggregate(let collection, let pipeline): - let docs = try await conn.aggregate(database: db, collection: collection, pipeline: pipeline) - return buildPluginResult(from: docs, startTime: startTime) + let result = try await conn.aggregate(database: db, collection: collection, pipeline: pipeline) + return buildPluginResult(from: result.docs, startTime: startTime, isTruncated: result.isTruncated) case .countDocuments(let collection, let filter): let count = try await conn.countDocuments(database: db, collection: collection, filter: filter) diff --git a/Plugins/RedisDriverPlugin/RedisCommandParser.swift b/Plugins/RedisDriverPlugin/RedisCommandParser.swift index d53a564b..040106d8 100644 --- a/Plugins/RedisDriverPlugin/RedisCommandParser.swift +++ b/Plugins/RedisDriverPlugin/RedisCommandParser.swift @@ -88,8 +88,8 @@ extension RedisParseError: PluginDriverError { var pluginErrorMessage: String { switch self { case .emptySyntax: return String(localized: "Empty Redis command") - case .invalidArgument(let msg): return msg - case .missingArgument(let msg): return msg + case .invalidArgument(let msg): return String(localized: "Invalid argument: \(msg)") + case .missingArgument(let msg): return String(localized: "Missing argument: \(msg)") } } } diff --git a/Plugins/TableProPluginKit/PluginConcurrencySupport.swift b/Plugins/TableProPluginKit/PluginConcurrencySupport.swift index cbba00ce..cf51a558 100644 --- a/Plugins/TableProPluginKit/PluginConcurrencySupport.swift +++ b/Plugins/TableProPluginKit/PluginConcurrencySupport.swift @@ -32,7 +32,7 @@ public func pluginDispatchAsync( } } -public func pluginDispatchAsync( +public func pluginDispatchAsyncCancellable( on queue: DispatchQueue, cancellationCheck: (@Sendable () -> Bool)? = nil, execute work: @escaping @Sendable () throws -> T diff --git a/Plugins/TableProPluginKit/PluginDriverError.swift b/Plugins/TableProPluginKit/PluginDriverError.swift index 36391850..9581028e 100644 --- a/Plugins/TableProPluginKit/PluginDriverError.swift +++ b/Plugins/TableProPluginKit/PluginDriverError.swift @@ -19,14 +19,14 @@ public extension PluginDriverError { var errorDescription: String? { var desc = pluginErrorMessage - if let code = pluginErrorCode { + if let code = pluginErrorCode, code != 0 { desc = "[\(code)] \(desc)" } if let state = pluginSqlState { desc += " (SQLSTATE: \(state))" } if let detail = pluginErrorDetail, !detail.isEmpty { - desc += "\n\(detail)" + desc += "\nDetail: \(detail)" } return desc }