Skip to content

Commit 856b116

Browse files
committed
refactor(create-database): server-driven dialog (fixes #927)
1 parent 0129afb commit 856b116

46 files changed

Lines changed: 935 additions & 293 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- PostgreSQL ICU collation provider in Create Database (PG 15+). Provider picker is added when the server reports PG 15 or newer. ICU locale list comes from `pg_collation`. SQL emission is version-aware: PG 16+ uses unified `LOCALE`, PG 15 uses `ICU_LOCALE` with `LC_COLLATE 'C' LC_CTYPE 'C'`.
1213
- Connection URL parsing: SSH `user:password@host` split, `safeModeLevel` from TablePlus URLs, case-insensitive query params
1314
- Connection URL export: SSH password, Redis database index, MongoDB auth params (`authSource`, `authMechanism`, `replicaSet`), and multi-host
1415
- SSH Private Key auth resolves keys from `~/.ssh/config` and default locations (`id_ed25519`, `id_rsa`, `id_ecdsa`) when no explicit key path is set
@@ -22,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2223

2324
### Changed
2425

26+
- Create Database dialog is now driver-driven. Each driver discovers its own valid options (PostgreSQL queries `pg_collation` and `pg_database`, MySQL/MariaDB query `information_schema.character_sets`/`collations`). The hardcoded macOS-flavored locale list is gone. Engines that don't support creation hide the Create button instead of failing on click.
2527
- Introduced TableRows, Row, and Delta value types in TablePro/Models/Query/ as the foundation for the data grid row model rewrite. No callers migrated yet (Phase C.1 of the DataGrid refactor).
2628
- DataChangeManager extracted a PendingChanges value type that owns cross-collection invariants for cell edits, row insertions, and deletions. DataChangeManager kept undo/redo registration, plugin SQL generation, and the `@Observable` boundary, dropping from ~960 to ~190 lines. The serialization DTO `TabPendingChanges` is renamed to `TabChangeSnapshot` to distinguish it from the live tracker.
2729
- AnyChangeManager uses ChangeManaging protocol instead of closure-based type erasure, removing all runtime `[Any]` downcasts
@@ -49,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4951
- Connection form: `usePrivateKey=true` from URL no longer disables Test/Create buttons
5052
- Transient connections from URL clean up keychain entries on connection failure
5153
- Native Search Field focus regression when clearing text
54+
- PostgreSQL Create Database failed with `new collation incompatible with template database` on glibc-initialized servers (#927). Encodings, collations, and the `template1` defaults are now read from the server. `LC_CTYPE` mirrors `LC_COLLATE`, and `TEMPLATE template0` is added automatically when the chosen collation differs from `template1.datcollate`.
55+
- Redshift Create Database emitted PostgreSQL `LC_COLLATE` syntax which is invalid Redshift grammar. Now emits `COLLATE { CASE_SENSITIVE | CASE_INSENSITIVE }`.
5256

5357
## [0.36.0] - 2026-04-27
5458

Plugins/BigQueryDriverPlugin/BigQueryPluginDriver.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -805,9 +805,13 @@ internal final class BigQueryPluginDriver: PluginDatabaseDriver, @unchecked Send
805805
"CREATE OR REPLACE VIEW \(quoteIdentifier(viewName)) AS\nSELECT * FROM table_name;"
806806
}
807807

808-
func createDatabase(name: String, charset: String, collation: String?) async throws {
808+
func createDatabaseFormSpec() async throws -> PluginCreateDatabaseFormSpec? {
809+
PluginCreateDatabaseFormSpec(fields: [], footnote: nil)
810+
}
811+
812+
func createDatabase(_ request: PluginCreateDatabaseRequest) async throws {
809813
guard let conn = connection else { throw BigQueryError.notConnected }
810-
let escaped = name.replacingOccurrences(of: "`", with: "\\`")
814+
let escaped = request.name.replacingOccurrences(of: "`", with: "\\`")
811815
_ = try await conn.executeQuery("CREATE SCHEMA `\(escaped)`")
812816
}
813817

Plugins/BigQueryDriverPlugin/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<plist version="1.0">
44
<dict>
55
<key>TableProPluginKitVersion</key>
6-
<integer>7</integer>
6+
<integer>8</integer>
77
</dict>
88
</plist>

Plugins/CSVExportPlugin/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<plist version="1.0">
44
<dict>
55
<key>TableProPluginKitVersion</key>
6-
<integer>7</integer>
6+
<integer>8</integer>
77
</dict>
88
</plist>

Plugins/CassandraDriverPlugin/CassandraPlugin.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,8 +1237,12 @@ internal final class CassandraPluginDriver: PluginDatabaseDriver, @unchecked Sen
12371237
return databases.map { PluginDatabaseMetadata(name: $0) }
12381238
}
12391239

1240-
func createDatabase(name: String, charset: String, collation: String?) async throws {
1241-
let safeKs = escapeIdentifier(name)
1240+
func createDatabaseFormSpec() async throws -> PluginCreateDatabaseFormSpec? {
1241+
PluginCreateDatabaseFormSpec(fields: [], footnote: nil)
1242+
}
1243+
1244+
func createDatabase(_ request: PluginCreateDatabaseRequest) async throws {
1245+
let safeKs = escapeIdentifier(request.name)
12421246
let query = """
12431247
CREATE KEYSPACE "\(safeKs)"
12441248
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}

Plugins/CassandraDriverPlugin/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<key>CFBundleVersion</key>
2020
<string>$(CURRENT_PROJECT_VERSION)</string>
2121
<key>TableProPluginKitVersion</key>
22-
<integer>7</integer>
22+
<integer>8</integer>
2323
<key>NSPrincipalClass</key>
2424
<string>$(PRODUCT_MODULE_NAME).CassandraPlugin</string>
2525
</dict>

Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,12 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
579579
}
580580
}
581581

582-
func createDatabase(name: String, charset: String, collation: String?) async throws {
583-
let escapedName = name.replacingOccurrences(of: "`", with: "``")
582+
func createDatabaseFormSpec() async throws -> PluginCreateDatabaseFormSpec? {
583+
PluginCreateDatabaseFormSpec(fields: [], footnote: nil)
584+
}
585+
586+
func createDatabase(_ request: PluginCreateDatabaseRequest) async throws {
587+
let escapedName = request.name.replacingOccurrences(of: "`", with: "``")
584588
_ = try await execute(query: "CREATE DATABASE `\(escapedName)`")
585589
}
586590

Plugins/ClickHouseDriverPlugin/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<plist version="1.0">
44
<dict>
55
<key>TableProPluginKitVersion</key>
6-
<integer>7</integer>
6+
<integer>8</integer>
77
</dict>
88
</plist>

Plugins/CloudflareD1DriverPlugin/CloudflareD1PluginDriver.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,12 +500,16 @@ final class CloudflareD1PluginDriver: PluginDatabaseDriver, @unchecked Sendable
500500
PluginDatabaseMetadata(name: database)
501501
}
502502

503-
func createDatabase(name: String, charset: String, collation: String?) async throws {
503+
func createDatabaseFormSpec() async throws -> PluginCreateDatabaseFormSpec? {
504+
PluginCreateDatabaseFormSpec(fields: [], footnote: nil)
505+
}
506+
507+
func createDatabase(_ request: PluginCreateDatabaseRequest) async throws {
504508
guard let client = getClient() else {
505509
throw CloudflareD1Error.notConnected
506510
}
507511

508-
let newDb = try await client.createDatabase(name: name)
512+
let newDb = try await client.createDatabase(name: request.name)
509513

510514
lock.lock()
511515
databaseNameToUuid[newDb.name] = newDb.uuid

Plugins/CloudflareD1DriverPlugin/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<plist version="1.0">
44
<dict>
55
<key>TableProPluginKitVersion</key>
6-
<integer>7</integer>
6+
<integer>8</integer>
77
</dict>
88
</plist>

0 commit comments

Comments
 (0)