Skip to content

Commit bad1dca

Browse files
committed
Merge branch 'main' into feat/data-grid-font-setting
2 parents d50cbcd + 6c3da7d commit bad1dca

39 files changed

+470
-375
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Moved string literal escaping into plugin drivers via `escapeStringLiteral` on `PluginDatabaseDriver` and `DatabaseDriver` protocols; `SQLEscaping.escapeStringLiteral` now uses ANSI SQL escaping only (doubles single quotes, strips null bytes)
1313
- SQL autocomplete data types and CREATE TABLE options now use plugin-provided dialect data instead of hardcoded per-database switches
1414
- `FilterSQLGenerator` now uses `SQLDialectDescriptor` data (regex syntax, boolean literals, LIKE escape style, pagination style) instead of `DatabaseType` switch statements
15+
- Moved identifier quoting, autocomplete statement completions, view templates, and FK disable/enable into plugin system
16+
- Removed `DatabaseType` switches from `FilterSQLGenerator`, `SQLCompletionProvider`, `ImportDataSinkAdapter`, and `MainContentCoordinator+SidebarActions`
1517

1618
### Added
1719

1820
- Column visibility: toggle individual columns on/off via "Columns" button in the status bar or right-click header context menu "Hide Column", with per-tab and per-table persistence
1921
- `SQLDialectDescriptor` in TableProPluginKit: plugins can now self-describe their SQL dialect (keywords, functions, data types, identifier quoting), with `SQLDialectFactory` preferring plugin-provided dialect info over built-in structs
2022
- DDL schema generation protocol in TableProPluginKit: plugins can now optionally provide database-specific ALTER TABLE syntax (ADD/MODIFY/DROP COLUMN, ADD/DROP INDEX, ADD/DROP FK, MODIFY PK) via `PluginDatabaseDriver`, with `SchemaStatementGenerator` trying plugin methods first before falling back to built-in logic
2123
- Plugin-provided table operations: `truncateTableStatements`, `dropObjectStatement`, `foreignKeyDisableStatements`, `foreignKeyEnableStatements` in `PluginDatabaseDriver` protocol, allowing plugins to override TRUNCATE, DROP, and FK handling SQL
24+
- `CompletionEntry` struct and `statementCompletions` on `DriverPlugin` for plugin-provided autocomplete entries (MongoDB MQL methods, Redis commands)
25+
- `offsetFetchOrderBy` property on `SQLDialectDescriptor` for plugin-controlled ORDER BY in OFFSET/FETCH pagination
26+
- `createViewTemplate()`, `editViewFallbackTemplate(viewName:)`, and `castColumnToText(_:)` on `PluginDatabaseDriver` for plugin-provided view DDL templates and column casting
2227
- `buildExplainQuery` method in `PluginDatabaseDriver` protocol: plugins can now provide database-specific EXPLAIN syntax, with coordinator falling back to built-in logic when plugin returns nil
2328
- `SettablePlugin` protocol in TableProPluginKit SDK: unified settings pattern for all plugins with automatic persistence via `loadSettings()`/`saveSettings()`, replacing duplicated boilerplate across export/import/driver plugins
2429
- 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

Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,17 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
724724
"EXPLAIN \(sql)"
725725
}
726726

727+
// MARK: - View Templates
728+
729+
func createViewTemplate() -> String? {
730+
"CREATE VIEW view_name AS\nSELECT column1, column2\nFROM table_name\nWHERE condition;"
731+
}
732+
733+
func editViewFallbackTemplate(viewName: String) -> String? {
734+
let quoted = quoteIdentifier(viewName)
735+
return "CREATE OR REPLACE VIEW \(quoted) AS\nSELECT * FROM table_name;"
736+
}
737+
727738
// MARK: - Kill Query
728739

729740
private func killQuery(queryId: String) {

Plugins/DuckDBDriverPlugin/DuckDBPlugin.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,17 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
821821
"EXPLAIN \(sql)"
822822
}
823823

824+
// MARK: - View Templates
825+
826+
func createViewTemplate() -> String? {
827+
"CREATE OR REPLACE VIEW view_name AS\nSELECT column1, column2\nFROM table_name\nWHERE condition;"
828+
}
829+
830+
func editViewFallbackTemplate(viewName: String) -> String? {
831+
let quoted = quoteIdentifier(viewName)
832+
return "CREATE OR REPLACE VIEW \(quoted) AS\nSELECT * FROM table_name;"
833+
}
834+
824835
// MARK: - Private Helpers
825836

826837
nonisolated private func setInterruptHandle(_ handle: duckdb_connection?) {

Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,21 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
428428
return "[\(escaped)]"
429429
}
430430

431+
// MARK: - View Templates
432+
433+
func createViewTemplate() -> String? {
434+
"CREATE OR ALTER VIEW view_name AS\nSELECT column1, column2\nFROM table_name\nWHERE condition;"
435+
}
436+
437+
func editViewFallbackTemplate(viewName: String) -> String? {
438+
let quoted = quoteIdentifier(viewName)
439+
return "CREATE OR ALTER VIEW \(quoted) AS\nSELECT * FROM table_name;"
440+
}
441+
442+
func castColumnToText(_ column: String) -> String {
443+
"CAST(\(column) AS NVARCHAR(MAX))"
444+
}
445+
431446
init(config: DriverConnectionConfig) {
432447
self.config = config
433448
self._currentSchema = config.additionalFields["mssqlSchema"]?.isEmpty == false

Plugins/MongoDBDriverPlugin/MongoDBPlugin.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,33 @@ final class MongoDBPlugin: NSObject, TableProPlugin, DriverPlugin {
6868

6969
static let sqlDialect: SQLDialectDescriptor? = nil
7070

71+
static var statementCompletions: [CompletionEntry] {
72+
[
73+
CompletionEntry(label: "db.", insertText: "db."),
74+
CompletionEntry(label: "db.runCommand", insertText: "db.runCommand"),
75+
CompletionEntry(label: "db.adminCommand", insertText: "db.adminCommand"),
76+
CompletionEntry(label: "db.createView", insertText: "db.createView"),
77+
CompletionEntry(label: "db.createCollection", insertText: "db.createCollection"),
78+
CompletionEntry(label: "show dbs", insertText: "show dbs"),
79+
CompletionEntry(label: "show collections", insertText: "show collections"),
80+
CompletionEntry(label: ".find", insertText: ".find"),
81+
CompletionEntry(label: ".findOne", insertText: ".findOne"),
82+
CompletionEntry(label: ".aggregate", insertText: ".aggregate"),
83+
CompletionEntry(label: ".insertOne", insertText: ".insertOne"),
84+
CompletionEntry(label: ".insertMany", insertText: ".insertMany"),
85+
CompletionEntry(label: ".updateOne", insertText: ".updateOne"),
86+
CompletionEntry(label: ".updateMany", insertText: ".updateMany"),
87+
CompletionEntry(label: ".deleteOne", insertText: ".deleteOne"),
88+
CompletionEntry(label: ".deleteMany", insertText: ".deleteMany"),
89+
CompletionEntry(label: ".replaceOne", insertText: ".replaceOne"),
90+
CompletionEntry(label: ".findOneAndUpdate", insertText: ".findOneAndUpdate"),
91+
CompletionEntry(label: ".findOneAndReplace", insertText: ".findOneAndReplace"),
92+
CompletionEntry(label: ".findOneAndDelete", insertText: ".findOneAndDelete"),
93+
CompletionEntry(label: ".countDocuments", insertText: ".countDocuments"),
94+
CompletionEntry(label: ".createIndex", insertText: ".createIndex")
95+
]
96+
}
97+
7198
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
7299
MongoDBPluginDriver(config: config)
73100
}

Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,17 @@ final class MongoDBPluginDriver: PluginDatabaseDriver {
499499
}
500500
}
501501

502+
// MARK: - View Templates
503+
504+
func createViewTemplate() -> String? {
505+
"db.createView(\"view_name\", \"source_collection\", [\n {\"$match\": {}},\n {\"$project\": {\"_id\": 1}}\n])"
506+
}
507+
508+
func editViewFallbackTemplate(viewName: String) -> String? {
509+
let escaped = viewName.replacingOccurrences(of: "\"", with: "\\\"")
510+
return "db.runCommand({\"collMod\": \"\(escaped)\", \"viewOn\": \"source_collection\", \"pipeline\": [{\"$match\": {}}]})"
511+
}
512+
502513
// MARK: - Query Building
503514

504515
func buildBrowseQuery(

Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,31 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
602602
"EXPLAIN \(sql)"
603603
}
604604

605+
// MARK: - View Templates
606+
607+
func createViewTemplate() -> String? {
608+
"CREATE VIEW view_name AS\nSELECT column1, column2\nFROM table_name\nWHERE condition;"
609+
}
610+
611+
func editViewFallbackTemplate(viewName: String) -> String? {
612+
let quoted = quoteIdentifier(viewName)
613+
return "ALTER VIEW \(quoted) AS\nSELECT * FROM table_name;"
614+
}
615+
616+
func castColumnToText(_ column: String) -> String {
617+
"CAST(\(column) AS CHAR)"
618+
}
619+
620+
// MARK: - Foreign Key Checks
621+
622+
func foreignKeyDisableStatements() -> [String]? {
623+
["SET FOREIGN_KEY_CHECKS=0"]
624+
}
625+
626+
func foreignKeyEnableStatements() -> [String]? {
627+
["SET FOREIGN_KEY_CHECKS=1"]
628+
}
629+
605630
// MARK: - Private Helpers
606631

607632
private func extractTableName(from query: String) -> String? {

Plugins/OracleDriverPlugin/OraclePlugin.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ final class OraclePlugin: NSObject, TableProPlugin, DriverPlugin {
8686
regexSyntax: .regexpLike,
8787
booleanLiteralStyle: .numeric,
8888
likeEscapeStyle: .explicit,
89-
paginationStyle: .offsetFetch
89+
paginationStyle: .offsetFetch,
90+
offsetFetchOrderBy: "ORDER BY 1"
9091
)
9192

9293
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
@@ -111,6 +112,17 @@ final class OraclePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
111112
self.config = config
112113
}
113114

115+
// MARK: - View Templates
116+
117+
func createViewTemplate() -> String? {
118+
"CREATE OR REPLACE VIEW view_name AS\nSELECT column1, column2\nFROM table_name\nWHERE condition;"
119+
}
120+
121+
func editViewFallbackTemplate(viewName: String) -> String? {
122+
let quoted = quoteIdentifier(viewName)
123+
return "CREATE OR REPLACE VIEW \(quoted) AS\nSELECT * FROM table_name;"
124+
}
125+
114126
// MARK: - Connection
115127

116128
func connect() async throws {

Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ final class PostgreSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
166166
"EXPLAIN \(sql)"
167167
}
168168

169+
// MARK: - View Templates
170+
171+
func createViewTemplate() -> String? {
172+
"CREATE OR REPLACE VIEW view_name AS\nSELECT column1, column2\nFROM table_name\nWHERE condition;"
173+
}
174+
175+
func editViewFallbackTemplate(viewName: String) -> String? {
176+
let quoted = quoteIdentifier(viewName)
177+
return "CREATE OR REPLACE VIEW \(quoted) AS\nSELECT * FROM table_name;"
178+
}
179+
180+
func castColumnToText(_ column: String) -> String {
181+
"CAST(\(column) AS TEXT)"
182+
}
183+
169184
// MARK: - Schema
170185

171186
func fetchTables(schema: String?) async throws -> [PluginTableInfo] {

Plugins/RedisDriverPlugin/RedisPlugin.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,49 @@ final class RedisPlugin: NSObject, TableProPlugin, DriverPlugin {
5858

5959
static let sqlDialect: SQLDialectDescriptor? = nil
6060

61+
static var statementCompletions: [CompletionEntry] {
62+
[
63+
CompletionEntry(label: "GET", insertText: "GET"),
64+
CompletionEntry(label: "SET", insertText: "SET"),
65+
CompletionEntry(label: "DEL", insertText: "DEL"),
66+
CompletionEntry(label: "EXISTS", insertText: "EXISTS"),
67+
CompletionEntry(label: "KEYS", insertText: "KEYS"),
68+
CompletionEntry(label: "HGET", insertText: "HGET"),
69+
CompletionEntry(label: "HSET", insertText: "HSET"),
70+
CompletionEntry(label: "HGETALL", insertText: "HGETALL"),
71+
CompletionEntry(label: "HDEL", insertText: "HDEL"),
72+
CompletionEntry(label: "LPUSH", insertText: "LPUSH"),
73+
CompletionEntry(label: "RPUSH", insertText: "RPUSH"),
74+
CompletionEntry(label: "LRANGE", insertText: "LRANGE"),
75+
CompletionEntry(label: "LLEN", insertText: "LLEN"),
76+
CompletionEntry(label: "SADD", insertText: "SADD"),
77+
CompletionEntry(label: "SMEMBERS", insertText: "SMEMBERS"),
78+
CompletionEntry(label: "SREM", insertText: "SREM"),
79+
CompletionEntry(label: "SCARD", insertText: "SCARD"),
80+
CompletionEntry(label: "ZADD", insertText: "ZADD"),
81+
CompletionEntry(label: "ZRANGE", insertText: "ZRANGE"),
82+
CompletionEntry(label: "ZREM", insertText: "ZREM"),
83+
CompletionEntry(label: "ZSCORE", insertText: "ZSCORE"),
84+
CompletionEntry(label: "EXPIRE", insertText: "EXPIRE"),
85+
CompletionEntry(label: "TTL", insertText: "TTL"),
86+
CompletionEntry(label: "PERSIST", insertText: "PERSIST"),
87+
CompletionEntry(label: "TYPE", insertText: "TYPE"),
88+
CompletionEntry(label: "SCAN", insertText: "SCAN"),
89+
CompletionEntry(label: "HSCAN", insertText: "HSCAN"),
90+
CompletionEntry(label: "SSCAN", insertText: "SSCAN"),
91+
CompletionEntry(label: "ZSCAN", insertText: "ZSCAN"),
92+
CompletionEntry(label: "INFO", insertText: "INFO"),
93+
CompletionEntry(label: "DBSIZE", insertText: "DBSIZE"),
94+
CompletionEntry(label: "FLUSHDB", insertText: "FLUSHDB"),
95+
CompletionEntry(label: "SELECT", insertText: "SELECT"),
96+
CompletionEntry(label: "INCR", insertText: "INCR"),
97+
CompletionEntry(label: "DECR", insertText: "DECR"),
98+
CompletionEntry(label: "APPEND", insertText: "APPEND"),
99+
CompletionEntry(label: "MGET", insertText: "MGET"),
100+
CompletionEntry(label: "MSET", insertText: "MSET")
101+
]
102+
}
103+
61104
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
62105
RedisPluginDriver(config: config)
63106
}

0 commit comments

Comments
 (0)