diff --git a/CHANGELOG.md b/CHANGELOG.md index 510191409..fcab135d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Windows Authentication (Kerberos) to SQL Server now works on macOS. The bundled SQL Server driver was built without Kerberos support, so every Windows-auth connect failed as if the credentials were wrong. (#1918) +- The MongoDB, Oracle, Cassandra, and Elasticsearch plugins failed to install on 0.58 with "Bundle failed to load executable". (#1917) ## [0.58.0] - 2026-07-18 diff --git a/CLAUDE.md b/CLAUDE.md index 630f8b486..75886551d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -102,7 +102,9 @@ When adding a new method to the driver protocol: add to `PluginDatabaseDriver` ( **PluginKit ABI (resilient)**: TableProPluginKit is built with `BUILD_LIBRARY_FOR_DISTRIBUTION = YES` (Swift Library Evolution), so its public ABI is resilient. The Swift runtime instantiates witness tables for already-built plugins and fills any requirement the plugin did not implement from the protocol's default, so a plugin built against an older PluginKit keeps loading under a newer app. -**Additive changes are binary-compatible and need NO version bump**: adding a requirement to `DriverPlugin` / `PluginDatabaseDriver` that has a default implementation, reordering requirements, adding a field to a non-`@frozen` transfer struct, or removing a requirement that defaulted to `nil`. +**Additive changes are binary-compatible and need NO version bump**: adding a requirement to `DriverPlugin` / `PluginDatabaseDriver` that has a default implementation, reordering requirements, or adding a field to a non-`@frozen` transfer struct. + +**Never remove a published protocol requirement, even one that defaulted to `nil`.** Library Evolution fills in requirements *added* after a plugin was built, but it cannot rescue a requirement *removed* out from under an already-built plugin. Removing one deletes both its method descriptor and its default-implementation symbol, and every shipped plugin that relied on the default hard-references both in its witness table, so it fails to load with "Bundle failed to load executable". If the app stops using a requirement, leave it in place with its default (it costs nothing). Removing it is a breaking change: bump `currentPluginKitVersion` and re-release every plugin. (#1917, and it broke MongoDB, Oracle, Cassandra, and Elasticsearch on 0.58.) **Adding a field to a transfer struct is additive ONLY if every existing public initializer keeps its exact signature.** Adding a parameter to an existing public init or function, even with a default value, replaces its mangled symbol and breaks every already-built plugin (this shipped in 0.49.0: `PluginQueryResult` gained `columnMeta:` on its init and every registry plugin failed to load with "Bundle failed to load executable"). Add a NEW overload for the new field and keep the old signature; mark the old overload `@_disfavoredOverload` so new code resolves to the full init while old binaries keep their symbol. Before any PluginKit change run `scripts/check-pluginkit-abi.sh` (see below) and act on the result: either the diff is additive (verify no symbol disappeared) or it is breaking (bump and re-release). diff --git a/Plugins/TableProPluginKit/PluginDatabaseDriver.swift b/Plugins/TableProPluginKit/PluginDatabaseDriver.swift index 7ab9f248c..a05af9886 100644 --- a/Plugins/TableProPluginKit/PluginDatabaseDriver.swift +++ b/Plugins/TableProPluginKit/PluginDatabaseDriver.swift @@ -168,6 +168,8 @@ public protocol PluginDatabaseDriver: AnyObject, Sendable { // EXPLAIN query building (optional) func buildExplainQuery(_ sql: String) -> String? + func injectRowLimit(_ sql: String, limit: Int) -> String? + func quoteIdentifier(_ name: String) -> String func escapeStringLiteral(_ value: String) -> String @@ -347,6 +349,8 @@ public extension PluginDatabaseDriver { func buildExplainQuery(_ sql: String) -> String? { nil } + func injectRowLimit(_ sql: String, limit: Int) -> String? { nil } + func createViewTemplate() -> String? { nil } func editViewFallbackTemplate(viewName: String) -> String? { nil } func castColumnToText(_ column: String) -> String { column } diff --git a/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift b/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift index 06c8765ec..ecbf5973c 100644 --- a/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift +++ b/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift @@ -32,6 +32,7 @@ struct PluginKitABIResilienceTests { #expect(driver.foreignKeyEnableStatements() == nil) #expect(driver.supportedMaintenanceOperations() == nil) #expect(driver.buildExplainQuery("SELECT 1") == nil) + #expect(driver.injectRowLimit("SELECT 1", limit: 100) == nil) #expect(driver.defaultExportQuery(table: "users") == nil) #expect(driver.createViewTemplate() == nil) #expect(driver.generateCreateTableSQL(definition: .init(tableName: "users", columns: [], primaryKeyColumns: [])) == nil)