Skip to content

Commit 7593152

Browse files
LocNguyenHuuclaude
andcommitted
fix: resolve merge conflicts with upstream/main
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2 parents 1260b68 + 07a2267 commit 7593152

40 files changed

Lines changed: 628 additions & 557 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- 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)
13+
- SQL autocomplete data types and CREATE TABLE options now use plugin-provided dialect data instead of hardcoded per-database switches
14+
- `FilterSQLGenerator` now uses `SQLDialectDescriptor` data (regex syntax, boolean literals, LIKE escape style, pagination style) instead of `DatabaseType` switch statements
15+
1016
### Added
1117

1218
- `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
@@ -28,10 +34,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2834
- `ParameterStyle` enum in TableProPluginKit: plugins declare `?` or `$1` placeholder style via `parameterStyle` property on `PluginDatabaseDriver`
2935
- DML statement generation in ClickHouse, MSSQL, and Oracle plugins via `generateStatements()` for database-specific UPDATE/DELETE syntax
3036
- Cassandra and ScyllaDB database support via DataStax C driver (downloadable plugin)
37+
- `quoteIdentifier` method on `PluginDatabaseDriver` and `DatabaseDriver` protocols: plugins provide database-specific identifier quoting (backticks for MySQL/SQLite/ClickHouse, brackets for MSSQL, double-quotes for PostgreSQL/Oracle/DuckDB, passthrough for MongoDB/Redis)
3138

3239
### Changed
3340

3441
- Moved MSSQL and Oracle pagination query building (`OFFSET...FETCH NEXT`) from `TableQueryBuilder` into their respective plugin drivers via `buildBrowseQuery`/`buildFilteredQuery`/`buildQuickSearchQuery`/`buildCombinedQuery` hooks
42+
- Moved identifier quoting from `DatabaseType` into plugin drivers via `quoteIdentifier` method on `PluginDatabaseDriver` protocol, with each plugin providing its own quoting style (backtick, bracket, double-quote, or passthrough)
3543

3644
### Fixed
3745

Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,14 @@ final class ClickHousePlugin: NSObject, TableProPlugin, DriverPlugin {
8484
"ENUM8", "ENUM16",
8585
"IPV4", "IPV6",
8686
"JSON", "BOOL"
87-
]
87+
],
88+
tableOptions: [
89+
"ENGINE=MergeTree()", "ORDER BY", "PARTITION BY", "SETTINGS"
90+
],
91+
regexSyntax: .match,
92+
booleanLiteralStyle: .numeric,
93+
likeEscapeStyle: .implicit,
94+
paginationStyle: .limit
8895
)
8996

9097
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
@@ -134,6 +141,25 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
134141
var serverVersion: String? { _serverVersion }
135142
var supportsSchemas: Bool { false }
136143
var supportsTransactions: Bool { false }
144+
145+
func quoteIdentifier(_ name: String) -> String {
146+
let escaped = name.replacingOccurrences(of: "`", with: "``")
147+
return "`\(escaped)`"
148+
}
149+
150+
func escapeStringLiteral(_ value: String) -> String {
151+
var result = value
152+
result = result.replacingOccurrences(of: "\\", with: "\\\\")
153+
result = result.replacingOccurrences(of: "'", with: "''")
154+
result = result.replacingOccurrences(of: "\n", with: "\\n")
155+
result = result.replacingOccurrences(of: "\r", with: "\\r")
156+
result = result.replacingOccurrences(of: "\t", with: "\\t")
157+
result = result.replacingOccurrences(of: "\0", with: "\\0")
158+
result = result.replacingOccurrences(of: "\u{08}", with: "\\b")
159+
result = result.replacingOccurrences(of: "\u{0C}", with: "\\f")
160+
result = result.replacingOccurrences(of: "\u{1A}", with: "\\Z")
161+
return result
162+
}
137163
func beginTransaction() async throws {}
138164
func commitTransaction() async throws {}
139165
func rollbackTransaction() async throws {}

Plugins/DuckDBDriverPlugin/DuckDBPlugin.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ final class DuckDBPlugin: NSObject, TableProPlugin, DriverPlugin {
8888
"DATE", "TIME", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "INTERVAL",
8989
"UUID", "JSON",
9090
"LIST", "MAP", "STRUCT", "UNION", "ENUM", "BIT"
91-
]
91+
],
92+
regexSyntax: .regexpMatches,
93+
booleanLiteralStyle: .truefalse,
94+
likeEscapeStyle: .explicit,
95+
paginationStyle: .limit
9296
)
9397

9498
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {

Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,14 @@ final class MSSQLPlugin: NSObject, TableProPlugin, DriverPlugin {
8080
"DATE", "TIME", "DATETIME", "DATETIME2", "SMALLDATETIME", "DATETIMEOFFSET",
8181
"BIT", "UNIQUEIDENTIFIER", "XML", "SQL_VARIANT",
8282
"ROWVERSION", "TIMESTAMP", "HIERARCHYID"
83-
]
83+
],
84+
tableOptions: [
85+
"ON", "CLUSTERED", "NONCLUSTERED", "WITH", "TEXTIMAGE_ON"
86+
],
87+
regexSyntax: .unsupported,
88+
booleanLiteralStyle: .numeric,
89+
likeEscapeStyle: .explicit,
90+
paginationStyle: .offsetFetch
8491
)
8592

8693
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
@@ -416,6 +423,11 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
416423
var supportsSchemas: Bool { true }
417424
var supportsTransactions: Bool { true }
418425

426+
func quoteIdentifier(_ name: String) -> String {
427+
let escaped = name.replacingOccurrences(of: "]", with: "]]")
428+
return "[\(escaped)]"
429+
}
430+
419431
init(config: DriverConnectionConfig) {
420432
self.config = config
421433
self._currentSchema = config.additionalFields["mssqlSchema"]?.isEmpty == false
@@ -1250,7 +1262,7 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
12501262
// MARK: - Query Building Helpers
12511263

12521264
private func mssqlQuoteIdentifier(_ identifier: String) -> String {
1253-
"[\(identifier.replacingOccurrences(of: "]", with: "]]"))]"
1265+
quoteIdentifier(identifier)
12541266
}
12551267

12561268
private func mssqlBuildOrderByClause(

Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver {
2020
func beginTransaction() async throws {}
2121
func commitTransaction() async throws {}
2222
func rollbackTransaction() async throws {}
23+
func quoteIdentifier(_ name: String) -> String { name }
2324

2425
init(config: DriverConnectionConfig) {
2526
self.config = config

Plugins/MySQLDriverPlugin/MySQLPlugin.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,15 @@ final class MySQLPlugin: NSObject, TableProPlugin, DriverPlugin {
7373
"BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB",
7474
"DATE", "TIME", "DATETIME", "TIMESTAMP", "YEAR",
7575
"ENUM", "SET", "JSON", "BOOL", "BOOLEAN"
76-
]
76+
],
77+
tableOptions: [
78+
"ENGINE=InnoDB", "DEFAULT CHARSET=utf8mb4", "COLLATE=utf8mb4_unicode_ci",
79+
"AUTO_INCREMENT=", "COMMENT=", "ROW_FORMAT="
80+
],
81+
regexSyntax: .regexp,
82+
booleanLiteralStyle: .numeric,
83+
likeEscapeStyle: .implicit,
84+
paginationStyle: .limit
7785
)
7886

7987
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {

Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
2424
var supportsSchemas: Bool { false }
2525
var supportsTransactions: Bool { true }
2626

27+
func quoteIdentifier(_ name: String) -> String {
28+
let escaped = name.replacingOccurrences(of: "`", with: "``")
29+
return "`\(escaped)`"
30+
}
31+
32+
func escapeStringLiteral(_ value: String) -> String {
33+
var result = value
34+
result = result.replacingOccurrences(of: "\\", with: "\\\\")
35+
result = result.replacingOccurrences(of: "'", with: "''")
36+
result = result.replacingOccurrences(of: "\n", with: "\\n")
37+
result = result.replacingOccurrences(of: "\r", with: "\\r")
38+
result = result.replacingOccurrences(of: "\t", with: "\\t")
39+
result = result.replacingOccurrences(of: "\0", with: "\\0")
40+
result = result.replacingOccurrences(of: "\u{08}", with: "\\b")
41+
result = result.replacingOccurrences(of: "\u{0C}", with: "\\f")
42+
result = result.replacingOccurrences(of: "\u{1A}", with: "\\Z")
43+
return result
44+
}
45+
2746
private static let tableNameRegex = try? NSRegularExpression(pattern: "(?i)\\bFROM\\s+[`\"']?([\\w]+)[`\"']?")
2847
private static let limitRegex = try? NSRegularExpression(pattern: "(?i)\\s+LIMIT\\s+\\d+(\\s*,\\s*\\d+)?")
2948
private static let offsetRegex = try? NSRegularExpression(pattern: "(?i)\\s+OFFSET\\s+\\d+")

Plugins/OracleDriverPlugin/OraclePlugin.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,14 @@ final class OraclePlugin: NSObject, TableProPlugin, DriverPlugin {
7979
"DATE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE",
8080
"INTERVAL YEAR TO MONTH", "INTERVAL DAY TO SECOND",
8181
"BOOLEAN", "ROWID", "UROWID", "XMLTYPE", "SDO_GEOMETRY"
82-
]
82+
],
83+
tableOptions: [
84+
"TABLESPACE", "PCTFREE", "INITRANS"
85+
],
86+
regexSyntax: .regexpLike,
87+
booleanLiteralStyle: .numeric,
88+
likeEscapeStyle: .explicit,
89+
paginationStyle: .offsetFetch
8390
)
8491

8592
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {

Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ final class PostgreSQLPlugin: NSObject, TableProPlugin, DriverPlugin {
8181
"CHAR", "CHARACTER", "VARCHAR", "TEXT",
8282
"DATE", "TIME", "TIMESTAMP", "TIMESTAMPTZ", "INTERVAL",
8383
"BOOLEAN", "BOOL", "JSON", "JSONB", "UUID", "BYTEA", "ARRAY"
84-
]
84+
],
85+
tableOptions: [
86+
"INHERITS", "PARTITION BY", "TABLESPACE", "WITH", "WITHOUT OIDS"
87+
],
88+
regexSyntax: .tilde,
89+
booleanLiteralStyle: .truefalse,
90+
likeEscapeStyle: .explicit,
91+
paginationStyle: .limit
8592
)
8693

8794
static func driverVariant(for databaseTypeId: String) -> String? {

Plugins/RedisDriverPlugin/RedisPluginDriver.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ final class RedisPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
2323
redisConnection?.serverVersion()
2424
}
2525

26+
func quoteIdentifier(_ name: String) -> String { name }
27+
2628
init(config: DriverConnectionConfig) {
2729
self.config = config
2830
}

0 commit comments

Comments
 (0)