Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Plugin UI/capability metadata: each driver plugin now self-declares brand color, connection mode, supported features, column types, URL schemes, and grouping strategy via the `DriverPlugin` protocol
- Copy as INSERT/UPDATE SQL statements from data grid context menu
- Plugin download count display in Browse Plugins — fetched from GitHub Releases API and cached for 1 hour
- MSSQL query cancellation (`cancelQuery`) and lock timeout (`applyQueryTimeout`) support
Expand Down
24 changes: 24 additions & 0 deletions Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ final class ClickHousePlugin: NSObject, TableProPlugin, DriverPlugin {
static let iconName = "bolt.fill"
static let defaultPort = 8123

// MARK: - UI/Capability Metadata

static let brandColorHex = "#FFD100"
static let supportsForeignKeys = false
static let systemDatabaseNames: [String] = ["information_schema", "INFORMATION_SCHEMA", "system"]
static let columnTypesByCategory: [String: [String]] = [
"Integer": [
"UInt8", "UInt16", "UInt32", "UInt64", "UInt128", "UInt256",
"Int8", "Int16", "Int32", "Int64", "Int128", "Int256"
],
"Float": ["Float32", "Float64", "Decimal", "Decimal32", "Decimal64", "Decimal128", "Decimal256"],
"String": ["String", "FixedString", "Enum8", "Enum16"],
"Date": ["Date", "Date32", "DateTime", "DateTime64"],
"Binary": [],
"Boolean": ["Bool"],
"JSON": ["JSON"],
"UUID": ["UUID"],
"Array": ["Array"],
"Map": ["Map"],
"Tuple": ["Tuple"],
"IP": ["IPv4", "IPv6"],
"Geo": ["Point", "Ring", "Polygon", "MultiPolygon"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
ClickHousePluginDriver(config: config)
}
Expand Down
26 changes: 26 additions & 0 deletions Plugins/DuckDBDriverPlugin/DuckDBPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,32 @@ final class DuckDBPlugin: NSObject, TableProPlugin, DriverPlugin {
static let iconName = "duckdb-icon"
static let defaultPort = 0

// MARK: - UI/Capability Metadata

static let requiresAuthentication = false
static let connectionMode: ConnectionMode = .fileBased
static let urlSchemes: [String] = ["duckdb"]
static let fileExtensions: [String] = ["duckdb", "db"]
static let brandColorHex = "#FFD900"
static let supportsDatabaseSwitching = false
static let systemDatabaseNames: [String] = ["information_schema", "pg_catalog"]
static let databaseGroupingStrategy: GroupingStrategy = .flat
static let columnTypesByCategory: [String: [String]] = [
"Integer": ["TINYINT", "SMALLINT", "INTEGER", "BIGINT", "HUGEINT", "UTINYINT", "USMALLINT", "UINTEGER", "UBIGINT"],
"Float": ["FLOAT", "DOUBLE", "DECIMAL", "NUMERIC"],
"String": ["VARCHAR", "TEXT", "CHAR", "BPCHAR"],
"Date": ["DATE", "TIME", "TIMESTAMP", "TIMESTAMPTZ", "TIMESTAMP_S", "TIMESTAMP_MS", "TIMESTAMP_NS", "INTERVAL"],
"Binary": ["BLOB", "BYTEA", "BIT", "BITSTRING"],
"Boolean": ["BOOLEAN"],
"JSON": ["JSON"],
"UUID": ["UUID"],
"List": ["LIST"],
"Struct": ["STRUCT"],
"Map": ["MAP"],
"Union": ["UNION"],
"Enum": ["ENUM"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
DuckDBPluginDriver(config: config)
}
Expand Down
18 changes: 18 additions & 0 deletions Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ final class MSSQLPlugin: NSObject, TableProPlugin, DriverPlugin {
ConnectionField(id: "mssqlSchema", label: "Schema", placeholder: "dbo")
]

// MARK: - UI/Capability Metadata

static let brandColorHex = "#E34517"
static let systemDatabaseNames: [String] = ["master", "tempdb", "model", "msdb"]
static let databaseGroupingStrategy: GroupingStrategy = .bySchema
static let columnTypesByCategory: [String: [String]] = [
"Integer": ["TINYINT", "SMALLINT", "INT", "BIGINT"],
"Float": ["FLOAT", "REAL", "DECIMAL", "NUMERIC", "MONEY", "SMALLMONEY"],
"String": ["CHAR", "VARCHAR", "TEXT", "NCHAR", "NVARCHAR", "NTEXT"],
"Date": ["DATE", "TIME", "DATETIME", "DATETIME2", "SMALLDATETIME", "DATETIMEOFFSET"],
"Binary": ["BINARY", "VARBINARY", "IMAGE"],
"Boolean": ["BIT"],
"XML": ["XML"],
"UUID": ["UNIQUEIDENTIFIER"],
"Spatial": ["GEOMETRY", "GEOGRAPHY"],
"Other": ["SQL_VARIANT", "TIMESTAMP", "ROWVERSION", "CURSOR", "TABLE", "HIERARCHYID"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
MSSQLPluginDriver(config: config)
}
Expand Down
22 changes: 22 additions & 0 deletions Plugins/MongoDBDriverPlugin/MongoDBPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@ final class MongoDBPlugin: NSObject, TableProPlugin, DriverPlugin {
ConnectionField(id: "mongoWriteConcern", label: "Write Concern", placeholder: "majority")
]

// MARK: - UI/Capability Metadata

static let requiresAuthentication = false
static let urlSchemes: [String] = ["mongodb", "mongodb+srv"]
static let brandColorHex = "#00ED63"
static let queryLanguageName = "MQL"
static let editorLanguage: EditorLanguage = .javascript
static let supportsForeignKeys = false
static let supportsSchemaEditing = false
static let databaseGroupingStrategy: GroupingStrategy = .flat
static let columnTypesByCategory: [String: [String]] = [
"String": ["string", "objectId", "regex"],
"Number": ["int", "long", "double", "decimal"],
"Date": ["date", "timestamp"],
"Binary": ["binData"],
"Boolean": ["bool"],
"Array": ["array"],
"Object": ["object"],
"Null": ["null"],
"Other": ["javascript", "minKey", "maxKey"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
MongoDBPluginDriver(config: config)
}
Expand Down
16 changes: 16 additions & 0 deletions Plugins/MySQLDriverPlugin/MySQLPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ final class MySQLPlugin: NSObject, TableProPlugin, DriverPlugin {
static let additionalConnectionFields: [ConnectionField] = []
static let additionalDatabaseTypeIds: [String] = ["MariaDB"]

// MARK: - UI/Capability Metadata

static let urlSchemes: [String] = ["mysql"]
static let brandColorHex = "#FF9500"
static let systemDatabaseNames: [String] = ["information_schema", "mysql", "performance_schema", "sys"]
static let columnTypesByCategory: [String: [String]] = [
"Integer": ["TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER", "BIGINT"],
"Float": ["FLOAT", "DOUBLE", "DECIMAL", "NUMERIC", "REAL"],
"String": ["CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET"],
"Date": ["DATE", "TIME", "DATETIME", "TIMESTAMP", "YEAR"],
"Binary": ["BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB", "BIT"],
"Boolean": ["BOOLEAN", "BOOL"],
"JSON": ["JSON"],
"Spatial": ["GEOMETRY", "POINT", "LINESTRING", "POLYGON"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
MySQLPluginDriver(config: config)
}
Expand Down
17 changes: 17 additions & 0 deletions Plugins/OracleDriverPlugin/OraclePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ final class OraclePlugin: NSObject, TableProPlugin, DriverPlugin {
ConnectionField(id: "oracleServiceName", label: "Service Name", placeholder: "ORCL")
]

// MARK: - UI/Capability Metadata

static let brandColorHex = "#C3160B"
static let systemDatabaseNames: [String] = ["SYS", "SYSTEM", "OUTLN", "DBSNMP", "APPQOSSYS", "WMSYS", "XDB"]
static let databaseGroupingStrategy: GroupingStrategy = .bySchema
static let columnTypesByCategory: [String: [String]] = [
"Integer": ["NUMBER", "INTEGER", "INT", "SMALLINT"],
"Float": ["FLOAT", "BINARY_FLOAT", "BINARY_DOUBLE", "DECIMAL", "NUMERIC", "REAL", "DOUBLE PRECISION"],
"String": ["VARCHAR2", "NVARCHAR2", "CHAR", "NCHAR", "CLOB", "NCLOB", "LONG"],
"Date": ["DATE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE", "INTERVAL YEAR TO MONTH", "INTERVAL DAY TO SECOND"],
"Binary": ["RAW", "LONG RAW", "BLOB", "BFILE"],
"Boolean": [],
"XML": ["XMLTYPE"],
"Spatial": ["SDO_GEOMETRY"],
"Other": ["ROWID", "UROWID"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
OraclePluginDriver(config: config)
}
Expand Down
23 changes: 23 additions & 0 deletions Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ final class PostgreSQLPlugin: NSObject, TableProPlugin, DriverPlugin {
static let additionalConnectionFields: [ConnectionField] = []
static let additionalDatabaseTypeIds: [String] = ["Redshift"]

// MARK: - UI/Capability Metadata

static let urlSchemes: [String] = ["postgresql", "postgres"]
static let brandColorHex = "#336791"
static let systemDatabaseNames: [String] = ["postgres", "template0", "template1"]
static let databaseGroupingStrategy: GroupingStrategy = .bySchema
static let columnTypesByCategory: [String: [String]] = [
"Integer": ["SMALLINT", "INTEGER", "BIGINT", "SERIAL", "BIGSERIAL", "SMALLSERIAL"],
"Float": ["REAL", "DOUBLE PRECISION", "NUMERIC", "DECIMAL", "MONEY"],
"String": ["CHARACTER VARYING", "VARCHAR", "CHARACTER", "CHAR", "TEXT", "NAME"],
"Date": ["DATE", "TIME", "TIMESTAMP", "TIMESTAMPTZ", "INTERVAL", "TIME WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE"],
"Binary": ["BYTEA"],
"Boolean": ["BOOLEAN"],
"JSON": ["JSON", "JSONB"],
"UUID": ["UUID"],
"Array": ["ARRAY"],
"Network": ["INET", "CIDR", "MACADDR", "MACADDR8"],
"Geometric": ["POINT", "LINE", "LSEG", "BOX", "PATH", "POLYGON", "CIRCLE"],
"Range": ["INT4RANGE", "INT8RANGE", "NUMRANGE", "TSRANGE", "TSTZRANGE", "DATERANGE"],
"Text Search": ["TSVECTOR", "TSQUERY"],
"XML": ["XML"]
]

static func driverVariant(for databaseTypeId: String) -> String? {
switch databaseTypeId {
case "PostgreSQL": return "PostgreSQL"
Expand Down
25 changes: 25 additions & 0 deletions Plugins/RedisDriverPlugin/RedisPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,31 @@ final class RedisPlugin: NSObject, TableProPlugin, DriverPlugin {
static let additionalConnectionFields: [ConnectionField] = []
static let additionalDatabaseTypeIds: [String] = []

// MARK: - UI/Capability Metadata

static let requiresAuthentication = false
static let urlSchemes: [String] = ["redis"]
static let brandColorHex = "#DC382D"
static let queryLanguageName = "Redis CLI"
static let editorLanguage: EditorLanguage = .bash
static let supportsForeignKeys = false
static let supportsSchemaEditing = false
static let supportsDatabaseSwitching = false
static let supportsImport = false
static let databaseGroupingStrategy: GroupingStrategy = .flat
static let defaultGroupName = "db0"
static let columnTypesByCategory: [String: [String]] = [
"String": ["string"],
"List": ["list"],
"Set": ["set"],
"Sorted Set": ["zset"],
"Hash": ["hash"],
"Stream": ["stream"],
"HyperLogLog": ["hyperloglog"],
"Bitmap": ["bitmap"],
"Geospatial": ["geo"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
RedisPluginDriver(config: config)
}
Expand Down
18 changes: 18 additions & 0 deletions Plugins/SQLiteDriverPlugin/SQLitePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ final class SQLitePlugin: NSObject, TableProPlugin, DriverPlugin {
static let iconName = "doc.fill"
static let defaultPort = 0

// MARK: - UI/Capability Metadata

static let requiresAuthentication = false
static let connectionMode: ConnectionMode = .fileBased
static let urlSchemes: [String] = ["sqlite"]
static let fileExtensions: [String] = ["db", "sqlite", "sqlite3"]
static let brandColorHex = "#003B57"
static let supportsDatabaseSwitching = false
static let databaseGroupingStrategy: GroupingStrategy = .flat
static let columnTypesByCategory: [String: [String]] = [
"Integer": ["INTEGER", "INT", "TINYINT", "SMALLINT", "MEDIUMINT", "BIGINT"],
"Float": ["REAL", "DOUBLE", "FLOAT", "NUMERIC", "DECIMAL"],
"String": ["TEXT", "VARCHAR", "CHARACTER", "CHAR", "CLOB", "NVARCHAR", "NCHAR"],
"Date": ["DATE", "TIME", "DATETIME", "TIMESTAMP"],
"Binary": ["BLOB"],
"Boolean": ["BOOLEAN"]
]

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
SQLitePluginDriver(config: config)
}
Expand Down
9 changes: 9 additions & 0 deletions Plugins/TableProPluginKit/ConnectionMode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//
// ConnectionMode.swift
// TableProPluginKit
//

public enum ConnectionMode: String, Codable, Sendable {
case network
case fileBased
}
54 changes: 54 additions & 0 deletions Plugins/TableProPluginKit/DriverPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,64 @@ public protocol DriverPlugin: TableProPlugin {
static func driverVariant(for databaseTypeId: String) -> String?

func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver

// MARK: - UI/Capability Metadata

static var requiresAuthentication: Bool { get }
static var connectionMode: ConnectionMode { get }
static var urlSchemes: [String] { get }
static var fileExtensions: [String] { get }
static var brandColorHex: String { get }
static var queryLanguageName: String { get }
static var editorLanguage: EditorLanguage { get }
static var supportsForeignKeys: Bool { get }
static var supportsSchemaEditing: Bool { get }
static var supportsDatabaseSwitching: Bool { get }
static var supportsSchemaSwitching: Bool { get }
static var supportsImport: Bool { get }
static var supportsExport: Bool { get }
static var supportsHealthMonitor: Bool { get }
static var systemDatabaseNames: [String] { get }
static var systemSchemaNames: [String] { get }
static var databaseGroupingStrategy: GroupingStrategy { get }
static var defaultGroupName: String { get }
static var columnTypesByCategory: [String: [String]] { get }
}

public extension DriverPlugin {
static var additionalConnectionFields: [ConnectionField] { [] }
static var additionalDatabaseTypeIds: [String] { [] }
static func driverVariant(for databaseTypeId: String) -> String? { nil }

// MARK: - UI/Capability Metadata Defaults

static var requiresAuthentication: Bool { true }
static var connectionMode: ConnectionMode { .network }
static var urlSchemes: [String] { [] }
static var fileExtensions: [String] { [] }
static var brandColorHex: String { "#808080" }
static var queryLanguageName: String { "SQL" }
static var editorLanguage: EditorLanguage { .sql }
static var supportsForeignKeys: Bool { true }
static var supportsSchemaEditing: Bool { true }
static var supportsDatabaseSwitching: Bool { true }
static var supportsSchemaSwitching: Bool { false }
static var supportsImport: Bool { true }
static var supportsExport: Bool { true }
static var supportsHealthMonitor: Bool { true }
static var systemDatabaseNames: [String] { [] }
static var systemSchemaNames: [String] { [] }
static var databaseGroupingStrategy: GroupingStrategy { .byDatabase }
static var defaultGroupName: String { "main" }
static var columnTypesByCategory: [String: [String]] {
[
"Integer": ["INTEGER", "INT", "SMALLINT", "BIGINT", "TINYINT"],
"Float": ["FLOAT", "DOUBLE", "DECIMAL", "NUMERIC", "REAL"],
"String": ["VARCHAR", "CHAR", "TEXT", "NVARCHAR", "NCHAR"],
"Date": ["DATE", "TIME", "DATETIME", "TIMESTAMP"],
"Binary": ["BLOB", "BINARY", "VARBINARY"],
"Boolean": ["BOOLEAN", "BOOL"],
"JSON": ["JSON"]
]
}
}
48 changes: 48 additions & 0 deletions Plugins/TableProPluginKit/EditorLanguage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// EditorLanguage.swift
// TableProPluginKit
//

public enum EditorLanguage: Sendable, Equatable {
case sql
case javascript
case bash
case custom(String)
}

extension EditorLanguage: Codable {
private enum CodingKeys: String, CodingKey {
case type
case value
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
switch type {
case "sql": self = .sql
case "javascript": self = .javascript
case "bash": self = .bash
case "custom":
let value = try container.decode(String.self, forKey: .value)
self = .custom(value)
default:
self = .custom(type)
}
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .sql:
try container.encode("sql", forKey: .type)
case .javascript:
try container.encode("javascript", forKey: .type)
case .bash:
try container.encode("bash", forKey: .type)
case .custom(let value):
try container.encode("custom", forKey: .type)
try container.encode(value, forKey: .value)
}
}
}
10 changes: 10 additions & 0 deletions Plugins/TableProPluginKit/GroupingStrategy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// GroupingStrategy.swift
// TableProPluginKit
//

public enum GroupingStrategy: String, Codable, Sendable {
case byDatabase
case bySchema
case flat
}
Loading
Loading