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))")