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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Inline dropdown picker when editing ENUM and SET columns, covering MySQL, MariaDB, PostgreSQL, ClickHouse, DuckDB, and MongoDB JSON-schema enums (#1283)
- Filter rows show an enum dropdown for `=` and `!=` operators on enum columns (#1283)

### Changed

- Drivers populate allowed enum values directly in column metadata instead of parsing them downstream
- PluginKit ABI bumped to version 13; all registry plugins need to be re-tagged

## [0.42.0] - 2026-05-16

### Added
Expand Down
2 changes: 1 addition & 1 deletion Plugins/BigQueryDriverPlugin/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>12</integer>
<integer>13</integer>
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
</dict>
Expand Down
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>12</integer>
<integer>13</integer>
<key>TableProProvidesExportFormatIds</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>12</integer>
<integer>13</integer>
</dict>
</plist>
17 changes: 15 additions & 2 deletions Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
isPrimaryKey: pkColumns.contains(name),
defaultValue: defaultValue,
extra: extra,
comment: (comment?.isEmpty == false) ? comment : nil
comment: (comment?.isEmpty == false) ? comment : nil,
allowedValues: EnumValueParser.parseClickHouseEnum(from: ClickHousePluginDriver.unwrapTypeWrappers(dataType))
)
}
}
Expand Down Expand Up @@ -402,13 +403,25 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
isPrimaryKey: pkLookup[tableName]?.contains(colName) == true,
defaultValue: defaultValue,
extra: extra,
comment: (comment?.isEmpty == false) ? comment : nil
comment: (comment?.isEmpty == false) ? comment : nil,
allowedValues: EnumValueParser.parseClickHouseEnum(from: ClickHousePluginDriver.unwrapTypeWrappers(dataType))
)
columnsByTable[tableName, default: []].append(colInfo)
}
return columnsByTable
}

static func unwrapTypeWrappers(_ value: String) -> String {
for prefix in ["Nullable(", "LowCardinality("] {
if value.hasPrefix(prefix), value.hasSuffix(")") {
let start = value.index(value.startIndex, offsetBy: prefix.count)
let end = value.index(before: value.endIndex)
return unwrapTypeWrappers(String(value[start..<end]))
}
}
return value
}

func fetchIndexes(table: String, schema: String?) async throws -> [PluginIndexInfo] {
let escapedTable = table.replacingOccurrences(of: "'", with: "''")
var indexes: [PluginIndexInfo] = []
Expand Down
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>12</integer>
<integer>13</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>ClickHouse</string>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/CloudflareD1DriverPlugin/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>12</integer>
<integer>13</integer>
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
</dict>
Expand Down
48 changes: 46 additions & 2 deletions Plugins/DuckDBDriverPlugin/DuckDBPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,7 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
let result = try await executeParameterized(query: query, parameters: [.text(schemaName), .text(table)])

let pkColumns = try await fetchPrimaryKeyColumns(table: table, schema: schemaName)
let enumMap = try await fetchEnumLabelMap(schema: schemaName)

return result.rows.compactMap { row in
guard let name = row[safe: 0]?.asText,
Expand All @@ -801,7 +802,8 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
dataType: dataType,
isNullable: isNullable,
isPrimaryKey: isPrimaryKey,
defaultValue: defaultValue
defaultValue: defaultValue,
allowedValues: resolveEnumValues(dataType: dataType, enumMap: enumMap)
)
}
}
Expand Down Expand Up @@ -833,6 +835,7 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
}
}

let enumMap = try await fetchEnumLabelMap(schema: schemaName)
var allColumns: [String: [PluginColumnInfo]] = [:]

for row in result.rows {
Expand All @@ -851,7 +854,8 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
dataType: dataType,
isNullable: isNullable,
isPrimaryKey: isPrimaryKey,
defaultValue: defaultValue
defaultValue: defaultValue,
allowedValues: resolveEnumValues(dataType: dataType, enumMap: enumMap)
)

allColumns[tableName, default: []].append(column)
Expand All @@ -860,6 +864,46 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
return allColumns
}

private func fetchEnumLabelMap(schema: String) async throws -> [String: [String]] {
let typeNamesQuery = """
SELECT type_name
FROM duckdb_types()
WHERE schema_name = $1 AND type_category = 'ENUM'
"""
let typeResult: PluginQueryResult
do {
typeResult = try await executeParameterized(query: typeNamesQuery, parameters: [.text(schema)])
} catch {
return [:]
}
let typeNames = typeResult.rows.compactMap { $0[safe: 0]?.asText }
guard !typeNames.isEmpty else { return [:] }

var map: [String: [String]] = [:]
for typeName in typeNames {
let quoted = "\"\(typeName.replacingOccurrences(of: "\"", with: "\"\""))\""
let valuesQuery = "SELECT UNNEST(enum_range(NULL::\(quoted)))::VARCHAR AS value"
let valuesResult: PluginQueryResult
do {
valuesResult = try await execute(query: valuesQuery)
} catch {
continue
}
let labels = valuesResult.rows.compactMap { $0[safe: 0]?.asText }
if !labels.isEmpty {
map[typeName] = labels
}
}
return map
}

private func resolveEnumValues(dataType: String, enumMap: [String: [String]]) -> [String]? {
if let values = enumMap[dataType], !values.isEmpty {
return values
}
return EnumValueParser.parseMySQLEnumOrSet(from: dataType)
}

func fetchIndexes(table: String, schema: String?) async throws -> [PluginIndexInfo] {
let schemaName = resolveSchema(schema)
let query = """
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>12</integer>
<integer>13</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 @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>12</integer>
<integer>13</integer>
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
</dict>
Expand Down
2 changes: 1 addition & 1 deletion Plugins/EtcdDriverPlugin/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>12</integer>
<integer>13</integer>
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
</dict>
Expand Down
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>12</integer>
<integer>13</integer>
<key>TableProProvidesExportFormatIds</key>
<array>
<string>json</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 @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>TableProPluginKitVersion</key>
<integer>12</integer>
<integer>13</integer>
<key>TableProMinAppVersion</key>
<string>0.42.0</string>
</dict>
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>12</integer>
<integer>13</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>12</integer>
<integer>13</integer>
</dict>
</plist>
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>12</integer>
<integer>13</integer>
</dict>
</plist>
38 changes: 37 additions & 1 deletion Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
filter: "{}", sort: nil, projection: nil, skip: 0, limit: 50
).docs

let enumMap = (try? await fetchJsonSchemaEnums(conn: conn, table: table)) ?? [:]

if docs.isEmpty {
return [
PluginColumnInfo(
Expand All @@ -176,11 +178,45 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
let typeName = bsonTypeToString(types[index])
return PluginColumnInfo(
name: name, dataType: typeName, isNullable: name != "_id", isPrimaryKey: name == "_id",
defaultValue: nil, extra: nil, charset: nil, collation: nil, comment: nil
defaultValue: nil, extra: nil, charset: nil, collation: nil, comment: nil,
allowedValues: enumMap[name]
)
}
}

private func fetchJsonSchemaEnums(conn: MongoDBConnection, table: String) async throws -> [String: [String]] {
let escaped = escapeJsonString(table)
let result = try await conn.runCommand(
"{\"listCollections\": 1, \"filter\": {\"name\": \"\(escaped)\"}}",
database: currentDb
)
guard let firstDoc = result.first,
let cursor = firstDoc["cursor"] as? [String: Any],
let firstBatch = cursor["firstBatch"] as? [[String: Any]],
let collInfo = firstBatch.first,
let options = collInfo["options"] as? [String: Any],
let validator = options["validator"] as? [String: Any],
let jsonSchema = validator["$jsonSchema"] as? [String: Any],
let properties = jsonSchema["properties"] as? [String: Any]
else { return [:] }

var map: [String: [String]] = [:]
for (colName, spec) in properties {
guard let specDict = spec as? [String: Any] else { continue }
if let enumValues = extractStringEnum(specDict["enum"]) {
map[colName] = enumValues
}
}
return map
}

private func extractStringEnum(_ value: Any?) -> [String]? {
guard let array = value as? [Any], !array.isEmpty else { return nil }
guard array.allSatisfy({ $0 is String }) else { return nil }
let strings = array.compactMap { $0 as? String }
return strings.isEmpty ? nil : strings
}

func fetchAllColumns(schema: String?) async throws -> [String: [PluginColumnInfo]] {
guard mongoConnection != nil else {
throw MongoDBPluginError.notConnected
Expand Down
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>12</integer>
<integer>13</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>MySQL</string>
Expand Down
8 changes: 6 additions & 2 deletions Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
let upperType = dataType.uppercased()
let normalizedType = (upperType.hasPrefix("ENUM(") || upperType.hasPrefix("SET("))
? dataType : upperType
let allowedValues = EnumValueParser.parseMySQLEnumOrSet(from: normalizedType)

return PluginColumnInfo(
name: name,
Expand All @@ -229,7 +230,8 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
extra: extra,
charset: charset,
collation: collation == "NULL" ? nil : collation,
comment: comment?.isEmpty == false ? comment : nil
comment: comment?.isEmpty == false ? comment : nil,
allowedValues: allowedValues
)
}
}
Expand Down Expand Up @@ -270,6 +272,7 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
let upperType = dataType.uppercased()
let normalizedType = (upperType.hasPrefix("ENUM(") || upperType.hasPrefix("SET("))
? dataType : upperType
let allowedValues = EnumValueParser.parseMySQLEnumOrSet(from: normalizedType)

let column = PluginColumnInfo(
name: name,
Expand All @@ -280,7 +283,8 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
extra: extra,
charset: charset,
collation: collation == "NULL" ? nil : collation,
comment: comment?.isEmpty == false ? comment : nil
comment: comment?.isEmpty == false ? comment : nil,
allowedValues: allowedValues
)

allColumns[tableName, default: []].append(column)
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>12</integer>
<integer>13</integer>
</dict>
</plist>
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>12</integer>
<integer>13</integer>
<key>TableProProvidesDatabaseTypeIds</key>
<array>
<string>PostgreSQL</string>
Expand Down
Loading
Loading