Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- PluginKit ABI 21. Every registry plugin needs rebuilding before or with this release.
- Export summary reports the warnings an export produced, instead of a bare "Export completed". (#2517)

### Fixed

- Silent fallback order when foreign keys between the exported tables form a cycle. (#2517)
- Foreign keys declared twice in a SQL export on MySQL, SQL Server, DuckDB, Snowflake, CockroachDB and Redshift, and as an unsupported `ALTER TABLE` on SQLite, libSQL and Cloudflare D1. (#2517)
- Foreign keys missing from Redshift's reconstructed `CREATE TABLE`.

## [0.71.0] - 2026-09-02

### Added
Expand Down
2 changes: 1 addition & 1 deletion Plugins/BeancountDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>Beancount</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/BigQueryDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/CSVExportPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesExportFormatIds</key>
<array>
<string>csv</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/CSVImportPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesImportFormatIds</key>
<array>
<string>csv</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/CassandraDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>NSPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).CassandraPlugin</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/ClickHouseDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>ClickHouse</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ final class CloudflareD1PluginDriver: PluginDatabaseDriver, @unchecked Sendable

var providesBulkForeignKeyFetch: Bool { true }

var tableDDLIncludesForeignKeys: Bool { true }

func fetchAllForeignKeys(schema: String?) async throws -> [String: [PluginForeignKeyInfo]] {
let query = """
SELECT m.name AS table_name, p.id, p."table" AS referenced_table,
Expand Down
2 changes: 1 addition & 1 deletion Plugins/CloudflareD1DriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/DamengDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>Dameng</string>
Expand Down
2 changes: 2 additions & 0 deletions Plugins/DuckDBDriverPlugin/DuckDBPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
}
}

var tableDDLIncludesForeignKeys: Bool { true }

func fetchForeignKeys(table: String, schema: String?) async throws -> [PluginForeignKeyInfo] {
let schemaName = resolveSchema(schema)
let catalog = try requireCatalog()
Expand Down
2 changes: 1 addition & 1 deletion Plugins/DuckDBDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/DynamoDBDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/ElasticsearchDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<key>TableProMinAppVersion</key>
<string>0.53.0</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/EtcdDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/JSONExportPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesExportFormatIds</key>
<array>
<string>json</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/JSONImportPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesImportFormatIds</key>
<array>
<string>json</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/KafkaDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>Kafka</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/LibSQLDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 2 additions & 0 deletions Plugins/LibSQLDriverPlugin/LibSQLPluginDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ final class LibSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {

var providesBulkForeignKeyFetch: Bool { true }

var tableDDLIncludesForeignKeys: Bool { true }

func fetchAllForeignKeys(schema: String?) async throws -> [String: [PluginForeignKeyInfo]] {
let query = """
SELECT m.name AS table_name, p.id, p."table" AS referenced_table,
Expand Down
2 changes: 1 addition & 1 deletion Plugins/MQLExportPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesExportFormatIds</key>
<array>
<string>mql</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/MSSQLDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 2 additions & 0 deletions Plugins/MSSQLDriverPlugin/MSSQLPluginDriver+Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ extension MSSQLPluginDriver {

var providesBulkForeignKeyFetch: Bool { true }

var tableDDLIncludesForeignKeys: Bool { true }

func fetchAllForeignKeys(schema: String?) async throws -> [String: [PluginForeignKeyInfo]] {
let esc = effectiveSchemaEscaped(schema)
let sql = """
Expand Down
2 changes: 1 addition & 1 deletion Plugins/MongoDBDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Plugins/MySQLDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>MySQL</string>
Expand Down
2 changes: 2 additions & 0 deletions Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {

var providesBulkForeignKeyFetch: Bool { true }

var tableDDLIncludesForeignKeys: Bool { true }

func fetchAllForeignKeys(schema: String?) async throws -> [String: [PluginForeignKeyInfo]] {
let dbName = _activeDatabase
let escapedDb = dbName.replacingOccurrences(of: "'", with: "''")
Expand Down
2 changes: 1 addition & 1 deletion Plugins/OracleDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</plist>
2 changes: 2 additions & 0 deletions Plugins/PostgreSQLDriverPlugin/CockroachPluginDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ final class CockroachPluginDriver: LibPQBackedDriver, @unchecked Sendable {
}
}

var tableDDLIncludesForeignKeys: Bool { true }

func fetchForeignKeys(table: String, schema: String?) async throws -> [PluginForeignKeyInfo] {
let safeTable = escapeLiteral(table)
let schemaLiteral = escapeLiteral(schema ?? core.currentSchema)
Expand Down
2 changes: 1 addition & 1 deletion Plugins/PostgreSQLDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>PostgreSQL</string>
Expand Down
33 changes: 32 additions & 1 deletion Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ final class RedshiftPluginDriver: LibPQBackedDriver, @unchecked Sendable {
return indexes
}

var tableDDLIncludesForeignKeys: Bool { true }

func fetchForeignKeys(table: String, schema: String?) async throws -> [PluginForeignKeyInfo] {
let safeTable = escapeLiteral(table)
let query = """
Expand Down Expand Up @@ -455,8 +457,11 @@ final class RedshiftPluginDriver: LibPQBackedDriver, @unchecked Sendable {
throw LibPQPluginError(message: "Failed to fetch DDL for table '\(table)'", sqlState: nil, detail: nil)
}

var parts = columnDefs
parts.append(contentsOf: try await foreignKeyClauses(table: table, schema: schema))

let ddl = "CREATE TABLE \(quotedSchema).\(quotedTable) (\n " +
columnDefs.joined(separator: ",\n ") +
parts.joined(separator: ",\n ") +
"\n);"

do {
Expand All @@ -479,6 +484,32 @@ final class RedshiftPluginDriver: LibPQBackedDriver, @unchecked Sendable {
return ddl
}

/// `SHOW TABLE` declares foreign keys inline, so the reconstruction below has to as well or
/// `tableDDLIncludesForeignKeys` would be true of one path and false of the other, and a SQL
/// export would drop every constraint whenever the fallback ran. A failed lookup therefore
/// throws rather than returning nothing, because nothing here is indistinguishable from a
/// table that has no foreign keys.
private func foreignKeyClauses(table: String, schema: String?) async throws -> [String] {
let foreignKeys = try await fetchForeignKeys(table: table, schema: schema)
var orderedNames: [String] = []
var grouped: [String: [PluginForeignKeyInfo]] = [:]
for foreignKey in foreignKeys {
if grouped[foreignKey.name] == nil { orderedNames.append(foreignKey.name) }
grouped[foreignKey.name, default: []].append(foreignKey)
}
return orderedNames.compactMap { name in
guard let group = grouped[name], let first = group.first else { return nil }
let columns = group.map { quoteIdentifier($0.column) }.joined(separator: ", ")
let referencedColumns = group.map { quoteIdentifier($0.referencedColumn) }.joined(separator: ", ")
let referencedSchema = first.referencedSchema.flatMap { $0.isEmpty ? nil : $0 }
let referencedTable = referencedSchema.map {
"\(quoteIdentifier($0)).\(quoteIdentifier(first.referencedTable))"
} ?? quoteIdentifier(first.referencedTable)
return "CONSTRAINT \(quoteIdentifier(name)) FOREIGN KEY (\(columns))"
+ " REFERENCES \(referencedTable) (\(referencedColumns))"
}
}

func fetchViewDefinition(view: String, schema: String?) async throws -> String {
let safeView = escapeLiteral(view)
let schemaLiteral = escapeLiteral(schema ?? core.currentSchema)
Expand Down
2 changes: 1 addition & 1 deletion Plugins/RedisDriverPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<key>NSPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).RedisPlugin</string>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>Redis</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/SQLExportPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>20</integer>
<integer>21</integer>
<key>TableProProvidesExportFormatIds</key>
<array>
<string>sql</string>
Expand Down
Loading
Loading