From fb510c83fe9695b0b34e86b335a63b1b46465280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 5 Jun 2026 12:22:33 +0700 Subject: [PATCH 1/3] fix(editor): parse quoted schema in schema-qualified table references --- .../Autocomplete/SQLContextAnalyzer.swift | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/TablePro/Core/Autocomplete/SQLContextAnalyzer.swift b/TablePro/Core/Autocomplete/SQLContextAnalyzer.swift index 48b0f68e1..ac86af057 100644 --- a/TablePro/Core/Autocomplete/SQLContextAnalyzer.swift +++ b/TablePro/Core/Autocomplete/SQLContextAnalyzer.swift @@ -246,15 +246,15 @@ final class SQLContextAnalyzer { private static let cteCommaRegex = compileRegex("(?i),\\s*([\\w]+)\\s+AS\\s*\\(") private static let tableRefRegexes: [NSRegularExpression] = { + let segment = "[`\"']?\\w+[`\"']?" + let path = "(\(segment)(?:\\.\(segment))*)" + let alias = "(?:\\s+(?:AS\\s+)?[`\"']?(\\w+)[`\"']?)?" let patterns = [ - "(?i)\\bFROM\\s+[`\"']?([\\w.]+)[`\"']?" + - "(?:\\s+(?:AS\\s+)?[`\"']?([\\w]+)[`\"']?)?", - "(?i)(?:LEFT|RIGHT|INNER|OUTER|CROSS|FULL)?\\s*(?:OUTER)?\\s*JOIN\\s+" + - "[`\"']?([\\w.]+)[`\"']?(?:\\s+(?:AS\\s+)?[`\"']?([\\w]+)[`\"']?)?", - "(?i)\\bUPDATE\\s+[`\"']?([\\w.]+)[`\"']?" + - "(?:\\s+(?:AS\\s+)?[`\"']?([\\w]+)[`\"']?)?", - "(?i)\\bINSERT\\s+INTO\\s+[`\"']?([\\w.]+)[`\"']?", - "(?i)\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+\\w+\\s+ON\\s+[`\"']?([\\w.]+)[`\"']?" + "(?i)\\bFROM\\s+\(path)\(alias)", + "(?i)(?:LEFT|RIGHT|INNER|OUTER|CROSS|FULL)?\\s*(?:OUTER)?\\s*JOIN\\s+\(path)\(alias)", + "(?i)\\bUPDATE\\s+\(path)\(alias)", + "(?i)\\bINSERT\\s+INTO\\s+\(path)", + "(?i)\\bCREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+\\w+\\s+ON\\s+\(path)" ] return patterns.map { compileRegex($0) } }() @@ -767,15 +767,7 @@ final class SQLContextAnalyzer { "JOIN", "ON", "AND", "OR", "WHERE", "SELECT", "FROM", "AS" ] - /// Strip schema prefix from a potentially schema-qualified name - private static func stripSchemaPrefix(_ raw: String) -> String { - let ns = raw as NSString - let dotRange = ns.range(of: ".", options: .backwards) - guard dotRange.location != NSNotFound else { return raw } - let start = dotRange.location + 1 - guard start < ns.length else { return raw } - return ns.substring(from: start) - } + private static let identifierQuoteChars = CharacterSet(charactersIn: "`\"'") /// Extract all table references (table names and aliases) from the query private func extractTableReferences(from query: String) -> [TableReference] { @@ -792,14 +784,13 @@ final class SQLContextAnalyzer { guard tableNSRange.location != NSNotFound else { return } let rawName = (query as NSString).substring(with: tableNSRange) - let tableName = Self.stripSchemaPrefix(rawName) + let segments = rawName.split(separator: ".").map { + String($0).trimmingCharacters(in: Self.identifierQuoteChars) + } + guard let tableName = segments.last, !tableName.isEmpty else { return } guard !Self.tableRefKeywords.contains(tableName.uppercased()) else { return } - let segments = rawName.split(separator: ".") - let schema = segments.count >= 2 - ? String(segments[segments.count - 2]) - .trimmingCharacters(in: CharacterSet(charactersIn: "`\"")) - : nil + let schema = segments.count >= 2 ? segments[segments.count - 2] : nil var alias: String? if match.numberOfRanges > 2 { From f0576e5c765a070834662ddb1f1f3fd8d9e993f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 5 Jun 2026 12:22:43 +0700 Subject: [PATCH 2/3] fix(connections): capture full output of a fast password command --- CHANGELOG.md | 1 + .../Core/Utilities/Connection/PasswordSourceResolver.swift | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d816abf5d..07e3add96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Running a PostgreSQL script with a `DO $$ ... $$` block or a dollar-quoted function body no longer fails with an unterminated dollar-quoted string error. (#1559) - AWS IAM connections no longer ask for a password on connect or reconnect. IAM supplies the credentials, so the prompt was never needed. The same now holds for any auth mode that replaces the password, such as a Postgres password file. - Oracle connection failures show the listener's actual reason (such as an unknown service name) instead of a generic "server closed the connection" message. (#483) +- A connection password read from a command no longer fails with an empty-password error when the command finishes quickly; its full output is now captured. ## [0.48.0] - 2026-06-02 diff --git a/TablePro/Core/Utilities/Connection/PasswordSourceResolver.swift b/TablePro/Core/Utilities/Connection/PasswordSourceResolver.swift index 45cb71a7c..4b75331bc 100644 --- a/TablePro/Core/Utilities/Connection/PasswordSourceResolver.swift +++ b/TablePro/Core/Utilities/Connection/PasswordSourceResolver.swift @@ -130,6 +130,11 @@ enum PasswordSourceResolver { stdoutPipe.fileHandleForReading.readabilityHandler = nil stderrPipe.fileHandleForReading.readabilityHandler = nil + let remainingStdout = stdoutPipe.fileHandleForReading.readDataToEndOfFile() + if !remainingStdout.isEmpty { stdoutCollector.append(remainingStdout) } + let remainingStderr = stderrPipe.fileHandleForReading.readDataToEndOfFile() + if !remainingStderr.isEmpty { stderrCollector.append(remainingStderr) } + if stdoutCollector.overflowed { throw ResolutionError.outputTooLarge } From d0248fc7d3ff5b884e5582841d8b87a652bfcb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 5 Jun 2026 12:22:57 +0700 Subject: [PATCH 3/3] fix(mcp): check cancellation and emit progress before resolving the connection --- CHANGELOG.md | 1 + TablePro/Core/MCP/Protocol/Tools/ExecuteQueryTool.swift | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07e3add96..835e1e0e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - AWS IAM connections no longer ask for a password on connect or reconnect. IAM supplies the credentials, so the prompt was never needed. The same now holds for any auth mode that replaces the password, such as a Postgres password file. - Oracle connection failures show the listener's actual reason (such as an unknown service name) instead of a generic "server closed the connection" message. (#483) - A connection password read from a command no longer fails with an empty-password error when the command finishes quickly; its full output is now captured. +- A cancelled MCP query request returns a cancelled error instead of an invalid-parameters error, and emits an initial progress notification when it starts. ## [0.48.0] - 2026-06-02 diff --git a/TablePro/Core/MCP/Protocol/Tools/ExecuteQueryTool.swift b/TablePro/Core/MCP/Protocol/Tools/ExecuteQueryTool.swift index f1c01d9a2..93ceb6091 100644 --- a/TablePro/Core/MCP/Protocol/Tools/ExecuteQueryTool.swift +++ b/TablePro/Core/MCP/Protocol/Tools/ExecuteQueryTool.swift @@ -77,6 +77,9 @@ public struct ExecuteQueryTool: MCPToolImplementation { throw MCPProtocolError.invalidParams(detail: "Query exceeds 100KB limit") } + try await throwIfCancelled(context) + await context.progress.emit(progress: 0.0, total: 1.0, message: "Connecting") + let meta = try await ToolConnectionMetadata.resolve(connectionId: connectionId) guard !QueryClassifier.isMultiStatement(query, databaseType: meta.databaseType) else { @@ -85,9 +88,6 @@ public struct ExecuteQueryTool: MCPToolImplementation { ) } - try await throwIfCancelled(context) - await context.progress.emit(progress: 0.0, total: 1.0, message: "Connecting") - if let database { _ = try await services.connectionBridge.switchDatabase( connectionId: connectionId,