Skill

SkillsData & Databases › Data engineering & pipelines

add-database-engine

Guided implementation for adding a new database engine to TablePro. Pre-loaded with all integration points, file locations, patterns, and the complete checklist derived from Redis implementation experience. Use when asked to add support for a new database type (e.g., Cassandra, DynamoDB, ClickHouse).

Freerisk: low
adddatabaseenginesqlmysqlredisschema

Tools: Foundation,OSLog,CNewDB

The full skill

— name: add-database-engine description: > Guided implementation for adding a new database engine to TablePro. Pre-loaded with all integration points, file locations, patterns, and the complete checklist derived from Redis implementation experience. Use when asked to add support for a new database type (e.g., Cassandra, DynamoDB, ClickHouse). autoTrigger: – "add.*database.*support" – "new.*database.*engine" – "implement.*driver" — # Add New Database Engine to TablePro Complete guide for adding a new database engine, based on the Redis implementation (35 files, 103+ integration points across 41 files). ## Overview: What a New Engine Requires | Layer | Files to Create | Files to Modify | |——-|—————-|—————–| | C Bridge (if native lib) | `CNewDB/` module | `project.pbxproj`, `Libs/` | | Connection | `NewDBConnection.swift` | — | | Driver | `NewDBDriver.swift`, `+ResultBuilding.swift` | `DatabaseDriver.swift` | | Core Utilities | `NewDBCommandParser.swift`, `NewDBQueryBuilder.swift`, `NewDBStatementGenerator.swift` | — | | Models | — | `DatabaseConnection.swift`, `ExportModels.swift`, `QueryTab.swift` | | Services | — | `ColumnType.swift`, `SQLDialectProvider.swift`, `TableQueryBuilder.swift`, `ExportService.swift`, `ImportService.swift`, `SQLEscaping.swift`, `FilterSQLGenerator.swift` | | Change Tracking | — | `DataChangeManager.swift`, `SQLStatementGenerator.swift` | | Coordinator | `MainContentCoordinator+NewDB.swift` | `MainContentCoordinator.swift`, `+Navigation.swift`, `+TableOperations.swift`, `+SidebarSave.swift` | | Views | — | `ConnectionFormView.swift`, `TableProToolbarView.swift`, `SidebarView.swift`, `DataGridView.swift`, `ExportDialog.swift`, `FilterPanelView.swift`, `SQLEditorView.swift`, `HighlightedSQLTextView.swift`, `SQLReviewPopover.swift`, `TypePickerContentView.swift`, `StructureRowProvider.swift` | | AI | — | `AISchemaContext.swift`, `AIPromptTemplates.swift`, `AIChatPanelView.swift` | | Other | — | `ContentView.swift`, `MainContentView.swift`, `Theme.swift`, `ConnectionURLParser.swift`, `ConnectionURLFormatter.swift`, `SQLParameterInliner.swift`, `SchemaStatementGenerator.swift` | | Tests | `NewDBTests/` directory | `TestFixtures.swift`, `DatabaseTypeTests.swift` | | Docs | `docs/databases/newdb.mdx`, `docs/vi/databases/newdb.mdx` | `docs/docs.json`, `docs/databases/overview.mdx`, `docs/vi/databases/overview.mdx` | | Build | `scripts/build-newdb-lib.sh` (if native) | `scripts/ci/prepare-libs.sh`, `scripts/build-release.sh` | — ## Phase 1: Foundation (C Bridge + Connection + Driver) ### 1a. C Bridge (only if using a C library) Create `TablePro/Core/Database/CNewDB/`: “` CNewDB/ ├── CNewDB.h # Umbrella header ├── module.modulemap # Swift module map └── include/ └── newdb/ # C library headers “` **module.modulemap pattern:** “`c module CNewDB { umbrella header "CNewDB.h" export * link "newdb" // Links against libNewDB.a } “` **Build static libs** — create `scripts/build-newdb-lib.sh`: – Build for arm64 and x86_64 separately – Create universal binary with `lipo -create` – Output to `Libs/libnewdb_universal.a` **Update Xcode project** — add to `project.pbxproj`: – Add CNewDB files to project – Add `Libs/libnewdb*.a` to Link Binary With Libraries – Add header search paths ### 1b. Connection Class **Create:** `TablePro/Core/Database/NewDBConnection.swift` Pattern from `RedisConnection.swift`: “`swift import Foundation import OSLog import CNewDB // if C bridge final class NewDBConnection: @unchecked Sendable { private static let logger = Logger(subsystem: "com.TablePro", category: "NewDBConnection") private let host: String private let port: Int // … connection parameters func connect() throws { … } func disconnect() { … } func execute(_ command: String) throws -> NewDBReply { … } } “` ### 1c. Driver **Create:** `TablePro/Core/Database/NewDBDriver.swift` Must conform to `DatabaseDriver` protocol. Key methods: “`swift final class NewDBDriver: DatabaseDriver { let connection: DatabaseConnection var status: ConnectionStatus = .disconnected var serverVersion: String? // Required protocol methods: func connect() async throws func disconnect() func testConnection() async throws -> Bool func applyQueryTimeout(_ seconds: Int) async throws func execute(query: String) async throws -> QueryResult func executeParameterized(query: String, parameters: [Any?]) async throws -> QueryResult func fetchRowCount(query: String) async throws -> Int func fetchRows(query: String, offset: Int, limit: Int) async throws -> QueryResult func fetchTables() async throws -> [TableInfo] func fetchColumns(table: String) async throws -> [ColumnInfo] func fetchAllColumns() async throws -> [String: [ColumnInfo]] func fetchIndexes(table: String) async throws -> [IndexInfo] func fetchTableMetadata(table: String) async throws -> TableMetadata? func fetchDatabases() async throws -> [String] func switchDatabase(_ name: String) async throws func fetchForeignKeys(table: String) async throws -> [ForeignKeyInfo] func fetchTriggers(table: String) async throws -> [TriggerInfo] } “` **Create:** `TablePro/Core/Database/NewDBDriver+ResultBuilding.swift` For non-SQL databases, build virtual table results: “`swift extension NewDBDriver { func buildBrowseResult(items: […]) -> QueryResult { // Map native data to columns/rows/columnTypes QueryResult( columns: ["col1", "col2", …], rows: mappedRows, columnTypes: [.text(rawType: "String"), …], affectedRows: count, metadata: nil ) } } “` **Column types for custom badges** — use rawType to customize `ColumnType.badgeLabel`: “`swift // In ColumnType.swift badgeLabel: case .text(let rawType): return rawType == "NewDBRaw" ? "custom-label" : "string" “` — ## Phase 2: Model & Enum Integration ### 2a. DatabaseType enum **File:** `TablePro/Models/DatabaseConnection.swift` (~line 100) Add case to `DatabaseType`: “`swift case newdb = "NewDB" “` Then update ALL switch statements on DatabaseType. Search with: “` Grep pattern="switch.*self|case \\.mysql" path="TablePro/" “` Properties to add in `DatabaseType`: – `iconName` → asset name – `displayName` → localized display name – `defaultPort` → default connection port – `quoteIdentifier(_:)` → identifier quoting style – `connectionURLScheme` → URL scheme for connection strings ### 2b. DatabaseConnection Add any engine-specific connection properties (e.g., `redisDatabase: Int` for Redis). ### 2c. ExportModels **File:** `TablePro/Models/ExportModels.swift` – Add export format support or exclusions for the new engine ### 2d. QueryTab **File:** `TablePro/Models/QueryTab.swift` – Add any engine-specific tab properties (e.g., `columnEnumValues` for Redis Type dropdown) — ## Phase 3: Core Services ### 3a. ColumnType badges **File:** `TablePro/Core/Services/ColumnType.swift` – Add rawType-based badge overrides in `badgeLabel` computed property ### 3b. SQLDialectProvider **File:** `TablePro/Core/Services/SQLDialectProvider.swift` – Add dialect for the new engine (keywords, functions, operators) ### 3c. TableQueryBuilder **File:** `TablePro/Core/Services/TableQueryBuilder.swift` – Add query building logic for browsing tables/data ### 3d. SQLEscaping **File:** `TablePro/Core/Database/SQLEscaping.swift` – Add escaping rules for the new engine's syntax ### 3e. FilterSQLGenerator **File:** `TablePro/Core/Database/FilterSQLGenerator.swift` – Add filter generation for the new engine ### 3f. Import/Export Services **Files:** `ExportService.swift`, `ImportService.swift` – Add support or explicit exclusion for the new engine — ## Phase 4: Change Tracking ### 4a. Statement Generator For SQL databases, modify `SQLStatementGenerator.swift`. For non-SQL databases, create a dedicated generator: **Create:** `TablePro/Core/NewDB/NewDBStatementGenerator.swift` Pattern from `RedisStatementGenerator.swift`: “`swift struct NewDBStatementGenerator { static func generateInsert(…) -> String { … } static func generateUpdate(…) -> String { … } static func generateDelete(…) -> String { … } } “` ### 4b. DataChangeManager **File:** `TablePro/Core/ChangeTracking/DataChangeManager.swift` – Add engine-specific logic in `configureForTable` if needed – Ensure `generateSQL()` routes to the correct statement generator ### 4c. Sidebar Save **File:** `TablePro/Views/Main/Extensions/MainContentCoordinator+SidebarSave.swift` CRITICAL: The right sidebar has `.keyboardShortcut("s", modifiers: .command)` which intercepts Cmd+S. The sidebar's `saveSidebarEdits()` must handle the new engine: “`swift if connection.type == .newdb { // Generate engine-specific commands statements += generateSidebarNewDBCommands(…) } else { // Existing SQL path } “` — ## Phase 5: Coordinator Integration ### 5a. MainContentCoordinator **File:** `TablePro/Views/Main/MainContentCoordinator.swift` Key integration points (search for `case .redis` to find all): 1. **~L381 explain prefix**: Add case for explain/analyze 2. **~L420 extractTableName**: Non-SQL engines need custom table name extraction 3. **~L1329 applyPhase1Result**: Set `isEditable`, `tableName`, `columnEnumValues` 4. **~L1361 configureForTable fallback**: Configure changeManager for engines without metadata ### 5b. Navigation **File:** `TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift` – Add navigation logic (sidebar click → query builder → browse data) **Create:** `TablePro/Views/Main/Extensions/MainContentCoordinator+NewDB.swift` – Engine-specific coordinator methods ### 5c. Table Operations **File:** `TablePro/Views/Main/Extensions/MainContentCoordinator+TableOperations.swift` – Add support for create/drop/rename operations — ## Phase 6: Views & UI ### 6a. Connection Form **File:** `TablePro/Views/Connection/ConnectionFormView.swift` – Add engine-specific fields (e.g., database selector for Redis db0-db15) ### 6b. Toolbar **File:** `TablePro/Views/Toolbar/TableProToolbarView.swift` – Hide/show toolbar items based on engine capabilities – Example: Redis hides Connection Switcher and Database Switcher buttons ### 6c. Menu Bar **File:** `TablePro/TableProApp.swift` – Disable irrelevant menu items (e.g., "Open Database…" for Redis) ### 6d. Data Grid **File:** `TablePro/Views/Results/DataGridView.swift` – Handle engine-specific cell editing rules – Handle enum dropdown for custom column types ### 6e. Other Views Files that commonly need `case .newdb` handling: – `SidebarView.swift` — sidebar display logic – `FilterPanelView.swift` — filter UI – `ExportDialog.swift` — export options – `SQLEditorView.swift` — editor configuration – `HighlightedSQLTextView.swift` — syntax highlighting – `SQLReviewPopover.swift` — SQL preview – `TypePickerContentView.swift` — type picker – `StructureRowProvider.swift` — structure view – `MainEditorContentView.swift` — editor content area – `ContentView.swift` — app layout – `MainContentView.swift` — main view — ## Phase 7: AI Integration – `AISchemaContext.swift` — schema context for AI – `AIPromptTemplates.swift` — prompt templates – `AIChatPanelView.swift` — chat panel — ## Phase 8: Utilities – `ConnectionURLParser.swift` — parse connection URLs – `ConnectionURLFormatter.swift` — format connection URLs – `SQLParameterInliner.swift` — parameter inlining – `SchemaStatementGenerator.swift` — schema DDL generation – `SQLCompletionProvider.swift` — autocomplete – `Theme.swift` — engine-specific theming — ## Phase 9: Tests Create test directory: `TableProTests/Core/NewDB/` Required test files (pattern from Redis): – `NewDBCommandParserTests.swift` – `NewDBQueryBuilderTests.swift` – `NewDBStatementGeneratorTests.swift` – `ColumnTypeNewDBTests.swift` – `ExportModelsNewDBTests.swift` Also update: – `TableProTests/Models/DatabaseTypeTests.swift` – `TableProTests/Helpers/TestFixtures.swift` — ## Phase 10: Documentation 1. Create `docs/databases/newdb.mdx` and `docs/vi/databases/newdb.mdx` 2. Update `docs/docs.json` — add page to navigation 3. Update `docs/databases/overview.mdx` and `docs/vi/databases/overview.mdx` 4. Update `docs/features/import-export.mdx` if applicable — ## Phase 11: Build & CI 1. Update `scripts/ci/prepare-libs.sh` — download/build native libs 2. Update `scripts/build-release.sh` — include new libs in release 3. Update `project.pbxproj` — add all new files to Xcode project — ## Implementation Strategy Use subagents with `isolation: "worktree"` for parallel work: **Wave 1 (Foundation):** C Bridge + Connection + Driver (sequential, depends on each other) **Wave 2 (Models — parallel):** – Agent A: `DatabaseConnection.swift` + `DatabaseType` enum updates – Agent B: `ColumnType.swift` + `ExportModels.swift` – Agent C: Core utilities (Parser, QueryBuilder, StatementGenerator) **Wave 3 (Integration — parallel):** – Agent A: `MainContentCoordinator.swift` + extensions – Agent B: `DataChangeManager.swift` + `SQLStatementGenerator.swift` + `SidebarSave.swift` – Agent C: Services (`SQLDialectProvider`, `TableQueryBuilder`, `SQLEscaping`, `FilterSQLGenerator`) **Wave 4 (Views — parallel):** – Agent A: `ConnectionFormView.swift` + `TableProToolbarView.swift` + `TableProApp.swift` – Agent B: `DataGridView.swift` + `SidebarView.swift` + `FilterPanelView.swift` – Agent C: Remaining views (editor, export, structure, AI) **Wave 5 (Tests + Docs — parallel):** – Agent A: All test files – Agent B: Documentation files **Wave 6 (Build verification):** “`bash xcodebuild -project TablePro.xcodeproj -scheme TablePro -configuration Debug build -skipPackagePluginValidation xcodebuild -project TablePro.xcodeproj -scheme TablePro test -skipPackagePluginValidation swiftlint lint –strict “` — ## Lessons from Redis Implementation 1. **Sidebar Cmd+S intercepts menu bar Cmd+S** — the right sidebar's `.keyboardShortcut("s")` takes priority. `saveSidebarEdits()` must handle the new engine, not just the main save path. 2. **`extractTableName(from:)` returns nil for non-SQL** — preserve `tableName` from the tab for non-SQL engines instead of parsing SQL. 3. **`configureForTable` requires metadata** — non-SQL engines won't have `metadata?.primaryKeyColumn`. Add a fallback to manually configure the changeManager with a known primary key. 4. **Toolbar items with `.opacity(0)` still occupy space** — use conditional `if` to completely remove toolbar items, not `.opacity(0)` or `.hidden()`. 5. **xcodebuild and Xcode IDE use different DerivedData** — debug logging may not appear if building with one but running with the other. 6. **Every `switch` on `DatabaseType` must be updated** — there are 100+ switch sites. Use `Grep pattern="case \\.mysql" path="TablePro/"` to find them all. 7. **Column type rawType drives badge labels** — use custom rawType strings (e.g., "RedisRaw", "RedisInt") and override in `ColumnType.badgeLabel` rather than adding new enum cases. 8. **`.enumType` column type triggers dropdown picker** — set `columnEnumValues[columnName]` on the tab to populate the picker values. — ## Quick Reference: File Count by Category | Category | New Files | Modified Files | |———-|———–|—————-| | Database Core | 3-5 | 2 | | Models | 0 | 3-4 | | Services | 0-1 | 6-8 | | Change Tracking | 1 | 2-3 | | Coordinator | 1 | 4-5 | | Views | 0 | 12-15 | | AI | 0 | 3 | | Utilities | 0 | 4-6 | | Tests | 5-8 | 2 | | Docs | 2 | 4 | | Build/CI | 1-2 | 2-3 | | **Total** | **~15-20** | **~45-55** |