From a84de0d25eefa435eb33349279e886da8ba643b3 Mon Sep 17 00:00:00 2001 From: salmonumbrella <182032677+salmonumbrella@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:53:42 -0700 Subject: [PATCH 1/2] feat: associate .psql and .pgsql files with the SQL editor --- CHANGELOG.md | 1 + TablePro/Info.plist | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87fe1d61e..f7b098cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - BigQuery datasets can be switched from the toolbar, the Cmd+K switcher, and the File menu, including creating and dropping datasets. (#509) +- `.psql` and `.pgsql` files are now associated with TablePro and open in the SQL editor, alongside `.sql`. ### Changed diff --git a/TablePro/Info.plist b/TablePro/Info.plist index 277bba8e2..2d097112e 100644 --- a/TablePro/Info.plist +++ b/TablePro/Info.plist @@ -14,6 +14,8 @@ CFBundleTypeExtensions sql + psql + pgsql CFBundleTypeIconSystemGenerated @@ -154,6 +156,8 @@ public.filename-extension sql + psql + pgsql From 1fa04a17a90b210b86391964bad4baa65af2dc9a 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: Wed, 10 Jun 2026 13:18:25 +0700 Subject: [PATCH 2/2] feat(editor): route .psql and .pgsql files through SQL open paths --- CHANGELOG.md | 2 +- .../Core/Services/Infrastructure/SQLFileService.swift | 11 +++++++++-- .../Core/Services/Infrastructure/URLClassifier.swift | 2 +- TablePro/Core/Services/SQL/SQLFolderWatcher.swift | 2 +- .../Services/Infrastructure/URLClassifierTests.swift | 6 +++--- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7b098cac..f148a8ea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - BigQuery datasets can be switched from the toolbar, the Cmd+K switcher, and the File menu, including creating and dropping datasets. (#509) -- `.psql` and `.pgsql` files are now associated with TablePro and open in the SQL editor, alongside `.sql`. +- `.psql` and `.pgsql` files now open in the SQL editor like `.sql`: Finder double-click, the open and save panels, and linked SQL folders all accept them. (#1641) ### Changed diff --git a/TablePro/Core/Services/Infrastructure/SQLFileService.swift b/TablePro/Core/Services/Infrastructure/SQLFileService.swift index 3580e4c90..837c444d3 100644 --- a/TablePro/Core/Services/Infrastructure/SQLFileService.swift +++ b/TablePro/Core/Services/Infrastructure/SQLFileService.swift @@ -13,6 +13,13 @@ import UniformTypeIdentifiers enum SQLFileService { private static let logger = Logger(subsystem: "com.TablePro", category: "SQLFileService") + static let supportedExtensions: Set = ["sql", "psql", "pgsql"] + + private static var allowedContentTypes: [UTType] { + let types = Set(supportedExtensions.compactMap { UTType(filenameExtension: $0) }) + return types.isEmpty ? [.plainText] : Array(types) + } + /// Reads a SQL file from disk. static func readFile(url: URL) async throws -> String { try await Task.detached { @@ -34,7 +41,7 @@ enum SQLFileService { @MainActor static func showOpenPanel() async -> [URL]? { let panel = NSOpenPanel() - panel.allowedContentTypes = [UTType(filenameExtension: "sql") ?? .plainText] + panel.allowedContentTypes = allowedContentTypes panel.allowsMultipleSelection = true panel.message = String(localized: "Select SQL files to open") let response = await panel.begin() @@ -46,7 +53,7 @@ enum SQLFileService { @MainActor static func showSavePanel(suggestedName: String = "query.sql") async -> URL? { let panel = NSSavePanel() - panel.allowedContentTypes = [UTType(filenameExtension: "sql") ?? .plainText] + panel.allowedContentTypes = allowedContentTypes panel.canCreateDirectories = true panel.nameFieldStringValue = suggestedName panel.message = String(localized: "Save SQL file") diff --git a/TablePro/Core/Services/Infrastructure/URLClassifier.swift b/TablePro/Core/Services/Infrastructure/URLClassifier.swift index 5f204a429..096c7b473 100644 --- a/TablePro/Core/Services/Infrastructure/URLClassifier.swift +++ b/TablePro/Core/Services/Infrastructure/URLClassifier.swift @@ -28,7 +28,7 @@ internal enum URLClassifier { if ext == "tablepro" { return .success(.openConnectionShare(url)) } - if ext == "sql" { + if SQLFileService.supportedExtensions.contains(ext) { return .success(.openSQLFile(url)) } if PluginManager.shared.allInspectorFileExtensions.contains(ext) { diff --git a/TablePro/Core/Services/SQL/SQLFolderWatcher.swift b/TablePro/Core/Services/SQL/SQLFolderWatcher.swift index b8197ea51..0f8498e9c 100644 --- a/TablePro/Core/Services/SQL/SQLFolderWatcher.swift +++ b/TablePro/Core/Services/SQL/SQLFolderWatcher.swift @@ -152,7 +152,7 @@ internal final class SQLFolderWatcher { var indexed: [LinkedSQLIndex.IndexedFile] = [] for case let url as URL in enumerator { - guard url.pathExtension.lowercased() == "sql" else { continue } + guard SQLFileService.supportedExtensions.contains(url.pathExtension.lowercased()) else { continue } let resourceValues = try? url.resourceValues(forKeys: [ .isRegularFileKey, .contentModificationDateKey, .fileSizeKey diff --git a/TableProTests/Core/Services/Infrastructure/URLClassifierTests.swift b/TableProTests/Core/Services/Infrastructure/URLClassifierTests.swift index c8a310caa..4e70193dd 100644 --- a/TableProTests/Core/Services/Infrastructure/URLClassifierTests.swift +++ b/TableProTests/Core/Services/Infrastructure/URLClassifierTests.swift @@ -50,9 +50,9 @@ struct URLClassifierTests { #expect(intent == nil) } - @Test("SQL file routes to openSQLFile") - func routesSQLFile() { - let sqlURL = URL(fileURLWithPath: "/tmp/query.sql") + @Test("SQL file routes to openSQLFile", arguments: ["sql", "psql", "pgsql", "PSQL"]) + func routesSQLFile(ext: String) { + let sqlURL = URL(fileURLWithPath: "/tmp/query.\(ext)") let intent = URLClassifier.classify(sqlURL) guard case .some(.success(.openSQLFile(let routed))) = intent else { Issue.record("Expected .openSQLFile, got \(String(describing: intent))")