All notable changes to Gridex are documented in this file. Gridex ships native apps for macOS (Swift/AppKit) and Windows (C++/WinUI 3). Unless noted otherwise, changes apply to both platforms.
- MongoDB filter & sort support — data grid filter panel now generates real BSON queries via
buildMongoFilter:- All operators supported:
=,≠,>,<,≥,≤,LIKE,NOT LIKE,IS NULL,IS NOT NULL,IN. LIKEpatterns translated to MongoDB$regex(case-insensitive,%→.*,_→.)._idfield auto-converts 24-char hex strings toObjectIdin filter values.- Multi-condition filters combined with
$and/$ormatching the combinator. - Sort applied via
.sort(Document)on the cursor.
- All operators supported:
- MongoDB nested field schema introspection —
collectFieldPathsrecursively flattens embedded documents into dot-notation paths (e.g.address.city). Depth capped at 10 to avoid stack overflow on pathological docs. Array fields are recorded at the parent level (not expanded totags.0) for more useful filter paths. - MongoDB array type detection —
inferTypenow returns"array"for BSON array documents instead of"document". - Backup/Restore: MongoDB NDJSON — pure Swift backup and restore without
mongodump/mongorestore:- Backup streams all collections via MongoKitten cursor, writes one JSON line per document:
{"_collection":"name","_doc":{...}}. - Restore parses NDJSON, groups by collection, batch-inserts via adapter.
- Format extension:
.ndjson.
- Backup streams all collections via MongoKitten cursor, writes one JSON line per document:
- Backup/Restore: Redis JSON snapshot — pure Swift backup and restore without
redis-cli:- Backup uses
SCAN+TYPE/GET/LRANGE/SMEMBERS/ZRANGE WITHSCORES/HGETALLto capture all key types (string, list, set, zset, hash) + TTL. Written as NDJSON viaRedisJSONSerializer. - Restore replays entries using
SET/RPUSH/SADD/ZADD/HSET+EXPIRE. - Format extension:
.json.
- Backup uses
- Backup/Restore: SQL Server native
.bak— uses SQL Server's built-inBACKUP DATABASE … TO DISKandRESTORE DATABASE … FROM DISK(T-SQL). Path is server-side; note for Docker users to volume-mount the path. RESTORE usesSINGLE_USER WITH ROLLBACK IMMEDIATE+WITH REPLACEpattern. RedisJSONSerializer— new helper that serializes/deserializesRedisAdapter.RedisKeyEntryto/from JSON strings. Handles all five Redis types with their native structures.
- AI Chat welcome screen — empty chat now shows a welcome screen instead of a blank message list. Dividers removed for a cleaner layout; window background color applied.
- MongoDB
documentToJSONString— BSON arrays now serialize as[…]instead of{…}by checkingdoc.isArray. Iteration switched fromfor pair in doctofor key in doc.keys+ subscript to avoid iterator quirks with unusual BSON encodings. - MongoDB query guard — added
guard parts.count >= 2before split oncollectionName.find()pattern to prevent out-of-bounds crash. - MongoDB fetchRows field paths —
_idis now always listed first; remaining fields collected withcollectFieldPaths(dot-notation aware) instead of flat per-document iteration.
- Microsoft SQL Server support — full integration as a 6th database type via CosmoMSSQL (pure Swift NIO, TDS 7.4, no FreeTDS dependency). Compatible with SQL Server 2014+, Azure SQL Edge, Azure SQL Database.
- Connection pool —
MSSQLConnectionPool(max 5 conns) avoids concurrent stream conflicts. - Schema introspection via
INFORMATION_SCHEMA— portable across all SQL Server variants. Lists databases, schemas (defaultdbo), tables, views, functions, procedures, columns, foreign keys. - CRUD with parameter inlining — works around CosmoMSSQL bug where RPC
sp_executesqlhardcodes transaction descriptor to 0. Inlines@pNplaceholders into SQL with proper escaping (N''for unicode strings,0xHEXfor binary, etc.) so transactions work correctly. - Multi-batch script execution — Query Editor splits scripts on
GOseparator. Uses dedicated script connection soUSEstatements persist across batches (e.g.CREATE DATABASE → USE → CREATE TABLEflows work). - Auto-refresh after
USE/CREATE DATABASE/DROP DATABASE— sidebar and database picker sync to new state. - MSSQL-specific dialect —
[brackets]identifier quoting,@pNplaceholders,OFFSET N ROWS FETCH NEXT M ROWS ONLYpagination. - MSSQL-aware ALTER TABLE — uses
ADD col type(noCOLUMN IF NOT EXISTS),EXEC sp_renamefor column rename,ALTER COLUMN col typefor type change. - Default schema
dbo— New Table tab defaults todboinstead ofpublicwhen adapter is MSSQL. - 37 native data types — full list from Microsoft Learn docs including modern
VARCHAR(MAX),NVARCHAR(MAX),JSON,HIERARCHYID,GEOGRAPHY, etc.
- Connection pool —
- Stored procedures support (MSSQL):
- Sidebar shows separate Procedures group with ▶ icon (green).
- Procedure detail view with parameter signature bar — chips show
@id INT,@name NVARCHAR(100),@result INT OUTPUTparsed fromINFORMATION_SCHEMA.PARAMETERS. - "Execute" button — auto-generates
EXEC [dbo].[procname] @p1 = NULL, @p2 = NULL OUTPUT;template and opens it in a new query tab ready to fill.
- SQL Editor smart script execution — detect multi-statement scripts and run sequentially. SQL Server uses
GObatch separator; other databases split on;(respecting quoted strings and--comments). Shows success status with statement count + rows affected for DDL/DML scripts. - Status banner separation — success info no longer shown as red error; uses green ✓ status banner with
N statement(s) executed · M row(s) affected · 0.50s.
- MSSQL connection error handling — wraps
SQLErrorenum inGridexError.queryExecutionFailedwithString(describing:)to preserve rich descriptions ("Server error 911: Database X does not exist") instead of generic "error 0". - MSSQL transaction commit — UPDATE/INSERT/DELETE inside transactions now work correctly via parameter inlining (CosmoMSSQL RPC bug workaround).
- MongoDB support — full integration as a 5th database type alongside PostgreSQL/MySQL/SQLite/Redis:
- MongoKitten driver — pure Swift NIO-based, no C dependencies. Port 27017 default.
- Connection string field — paste full
mongodb://ormongodb+srv://URI with Parse button to auto-fill host/port/user/password/database/SSL. SRV URIs (Atlas, DigitalOcean) preserved verbatim for proper DNS lookup. - Schema introspection — collections appear as tables in sidebar; fields inferred from sampled documents (string/integer/double/boolean/date/objectId/document).
- CRUD via data grid — Add row, edit cells, delete rows all route through MongoDB adapter using
insertOne/updateOne/deleteOne(no SQL generation). - Insert Document sheet — JSON editor with auto-template from detected fields. Supports MongoDB Extended JSON shortcuts (
$oid,$date). Allows inserting documents with any structure including new fields not in existing schema. - Database management — list/create/drop databases via shared connection pool. Database picker (⌘K) reconnects on switch.
- Schemaless structure view — Structure tab shows detected fields as read-only info instead of editable column form (no ALTER TABLE in MongoDB).
- Create Collection — simplified form (just collection name, no columns) for
New Tabletab when adapter is MongoDB. - Auto ObjectId detection — 24-char hex strings auto-converted to BSON ObjectId.
- SSH Tunnel support — NIOSSH-based local port forwarding. ConnectionManager auto-detects SSH config, establishes tunnel, and redirects database connection through
127.0.0.1:localPort. Password auth implemented, private key skeleton. - Delete Database — trash button in Database Switcher (⌘K) with confirmation dialog. Disabled for currently active database. Uses
DROP DATABASEfor PostgreSQL/MySQL. - General Settings — default page size (100/300/500/1000), confirm before delete toggle, auto-refresh sidebar with configurable interval, show query log default.
- Editor Settings — font size (8-24 with stepper), tab size (2/4/8), spaces vs tabs, word wrap, show line numbers, highlight current line.
- Dead import code — removed unused
ImportService.swiftandImportDataUseCase.swift. Import CSV/SQL already works directly via adapter in the UI views.
- Backup/Restore progress tracking — no more endless spinner:
- Restore (SQL): file piped through stdin in 64KB chunks, progress bar shows percentage based on bytes sent vs file size.
- Backup (pg_dump/mysqldump): polls output file size every 0.5s, shows written size + elapsed time (e.g.
12.5 MB · 8s). - Applies to PostgreSQL (psql, pg_dump) and MySQL (mysql, mysqldump).
- Build script (
scripts/build-app.sh) — builds.appbundle from SPM with compiled asset catalog, ad-hoc signing, and entitlements. Supports debug/release modes.
- Edit cell background — modified cell background (orange) only appears when value actually changes, not on every edit click. Fixed
justFinishedEditingpath not rebuilding pending change caches. - Edit cell blanks other columns — after editing a cell, other columns in the same row appeared blank. Fixed by force-redrawing all visible cells after edit ends.
- Cell background rendering —
dirtyRect.fill()replaced withbounds.fill()to ensure full cell background coverage. - Display cache sync —
displayCachenow updates on cell edit, date edit, new row insert, and discard changes. - App logo missing in built .app —
Bundle.module(SPM) could not findAssets.carin.appbundle. Added multi-path logo loader that tries.module, main bundle, and explicit resource path.
- Data grid scroll performance — eliminated per-frame bottlenecks:
- Pre-computed display string cache (
displayCache) avoids repeateddisplayStringcalls during scroll (600 array lookups vs 600 enum switches per frame). - Cached
NSAttributedStringin cells — reused acrossdraw()calls instead of recreating dictionary + layout every frame. - Pre-truncate text at 300 chars before Core Text layout; blob display as
"(BLOB N bytes)"with zero encoding cost. - Debounce increased 16ms → 100ms; column resize no longer triggers full visible-cell refresh.
- Dirty checking in
configureCell— only markneedsDisplaywhen cell content actually changed. - Removed
@Publishedfrom high-frequency properties (selectedRows,columnWidths,editingCell,insertedRowIndices) to prevent SwiftUI body re-evaluation on every click/resize. DateFormattershared singleton instead of allocating per cell.- Tooltip computed only on text change, not every
viewForcall.
- Pre-computed display string cache (
- Memory leak on tab close — RAM did not decrease when closing data tabs:
- Fixed Combine retain cycle: subscription held strong reference to
viewModel.objectWillChange, keepingDataGridViewStatealive indefinitely. - Coordinator
rowssnapshot now cleared viareleaseData(). DataGridViewState.rows,displayCache,columnsexplicitly cleared before removal from cache.queryEditorTextcleaned up oncloseAllTabs.
- Fixed Combine retain cycle: subscription held strong reference to
- Google Gemini AI provider — new LLM provider using OpenAI-compatible endpoint (
/v1beta/openai). Streaming chat, model fetching from API, API key validation. - AI Settings UI — configure provider (Gemini/Anthropic/OpenAI/Ollama), API key (Keychain), base URL, model picker with live fetch from API.
- AI Chat wired to real LLM — streaming responses with schema-aware system prompt. Replaces placeholder stub.
- Attach tables to chat —
+button in chat input to select tables/views. Schema is sent inline in user message for precise, per-question context. - Chat history in-memory — messages persist across tab switches (Details/Assistant), scoped per connection, cleared on disconnect.
- SettingsLink integration — gear icon and setup prompt in Assistant panel open macOS Settings window directly.
- Assistant tab always visible — no longer requires
aiPanelVisibletoggle; shows setup prompt when no API key configured.
- SQL Editor button in toolbar —
</>icon in the header toolbar for quick access to open a new query editor tab.
- Keyboard shortcuts not working (⌘K, ⌘⇧N, etc.) — menu commands were disabled even after connecting to a database. Root cause:
AppState.active(static weak var) is not reactive in SwiftUI, so.disabled()conditions were never re-evaluated. Fixed by using@FocusedObjectwith fallback toAppState.active.
- Truncate table — now uses pending workflow (orange highlight → commit) instead of executing immediately. SQLite uses
DELETE FROMsinceTRUNCATE TABLEis unsupported. Data grid refreshes automatically after commit. - Delete table cascade default — cascade option is now enabled by default in the delete confirmation sheet.
- Delete table error handling — failed DROP no longer silently removes the table from pending; it stays marked (red) and shows an error alert so users can retry or adjust options.
- App version in status bar — bottom-right corner shows current version (e.g.
v0.18.1), read from Info.plist.
- Connection with empty password — previously silently aborted when no keychain entry existed. Now connects normally (needed for Redis without AUTH, passwordless databases).
- Clearing saved password — when user empties the password field and saves, the old keychain entry is now deleted. Previously the stale password persisted and was sent on reconnect, causing AUTH errors on passwordless Redis servers.
- Rebrand: DataBridge → Gridex — app name, bundle ID (
com.gridex.app), all source code references, error types (GridexError), entitlements, and file names updated. - Project restructured:
DataBridge/→macos/, new emptywindow/folder for future Windows port. - Tests removed —
DataBridgeTests/deleted per project decision. - Package name and executable renamed to
Gridex.
- Redis pattern-based filter bar — single search field with glob patterns (
user:*,*session*), quick-filter presets menu, replaces SQL column filter for Redis connections. - ER Diagram toolbar button — quick access from header toolbar (SQL databases only).
- Critical crash fixes:
AppState.connect(): force unwrapgroup.next()!replaced with safeguard let(connection timeout crash).- Redis
HGETALL/ZRANGE WITHSCORESparsing: off-by-one instridedropped the last field/member. - Redis
insertRow/updateRow/deleteRow: keys with spaces broke command parsing — now uses directsendCommand()API. - PostgreSQL
updateRow/deleteRow: force unwrapvalues[$1]!replaced with safe.compactMap. ImportCSVSheet: array index access on empty CSV replaced with safe.first.- Redis TLS:
try!NIOSSLClientHandler replaced with properdo/catch.
- Redis safety:
- Collection fetches capped at 10,000 items (LRANGE/SSCAN/ZRANGE) to prevent OOM.
- SCAN loop checks connection state and caps at 100,000 keys.
- UI error handling:
TableStructureView,TableListView,ERDiagramViewModel,ConnectionFormPanelnow show errors to user with retry buttons instead of silentprint(). - ImportSQLWizard: hardcoded dark background color replaced with
NSColor.textBackgroundColor(light mode fix).
- Toolbar: all action buttons unified in single
primaryActiongroup; commit icon changed totext.insert; AI brain icon removed; console log placeholder usesterminalicon. - Structure tab hidden for Redis connections (key-value store has no table schema).
- Redis Server INFO — fixed RESP
\r\nline ending parsing that caused Server Info dashboard to show blank. Added error display for blocked commands. - Redis pipelined key loading —
fetchRows()now batches TYPE/TTL/value commands via NIO futures (~50x faster on cloud Redis). - Redis
listDatabases()— graceful fallback whenCONFIG GETis blocked (Upstash, ElastiCache).
- Add Key with type selection — create string, hash, list, set, or sorted set keys from a dedicated sheet with field editors and optional TTL.
- Key Detail View — double-click any key to open a dedicated tab showing:
- Hash: field/value table with inline add/delete
- List: indexed items
- Set: member list with add/remove
- Sorted Set: member/score table with add/remove
- String: full text view
- TTL management (set/remove) and key rename from the header bar
- Memory usage display (via
MEMORY USAGE)
- Server INFO Dashboard — new tab showing all Redis server sections (Server, Memory, Clients, Stats, Keyspace) with key metrics cards (memory, clients, uptime, ops/sec, hit rate). Auto-refresh toggle (5s interval).
- Slow Log Viewer — new tab displaying
SLOWLOG GETentries with ID, timestamp, duration, command, client. Reset button with confirmation. - DBSIZE in status bar — total key count shown when connected to Redis.
- Flush Database — toolbar action + sidebar context menu with destructive confirmation dialog.
- Key rename — from the Key Detail View header.
- Key duplicate — uses
COPY(Redis 6.2+) with type-aware fallback. - Redis CLI mode — Query Editor shows "Redis CLI" label, hides Format/Minify/Explain buttons (SQL-only).
- Redis sidebar context menu — Browse Keys, Add Key, Server Info, Slow Log, Flush Database.
- + Key button in DataGrid toolbar replaces "+ Row" for Redis connections.
- Toolbar buttons for Server Info and Slow Log (Redis-only, hidden for SQL databases).
- Redis TLS (rediss://) — connections to cloud Redis providers (Upstash, AWS ElastiCache, etc.) now work via NIOSSL. A "TLS (rediss://)" checkbox is shown in the Redis connection form.
- Password character count — secure password fields now display the number of characters (e.g. "36") so users can verify pasted passwords.
- Redis connection form cleanup — SSL keys row hidden for Redis; TLS toggle replaces SSL mode picker.
- New Redis database type alongside PostgreSQL, MySQL, and SQLite.
- RedisAdapter maps the key-value store into DataBridge's relational model:
- Virtual "Keys" table with columns:
key,type,value,ttl. - SCAN-based pagination for browsing keys with pattern-matching filters.
- Supports all Redis data types: string, list, set, sorted set, hash, stream.
- Raw Redis command execution via the query editor (e.g.
SET foo bar,HGETALL myhash). - RESP responses rendered as structured tables (arrays, key-value pairs, scalars).
- MULTI/EXEC/DISCARD transaction support.
- Database switching via
SELECT <db_number>(db0–db15).
- Virtual "Keys" table with columns:
- Connection form adapted for Redis: host, port, optional password, database number (0–15).
- Red gradient badge ("Rd") in Home screen and sidebar.
- RediStack (swift-server/RediStack) added as dependency.
- Cmd+N now opens a new window with the Home connection picker, allowing you to connect to a different database in a separate window.
- Each window has its own independent
AppState: separate connection, sidebar, tabs, and query history. - Windows operate fully in parallel — edit data in PostgreSQL in one window while querying MySQL in another.
- New Query shortcut moved from Cmd+N to Cmd+Shift+N (Cmd+N is now "New Window").
WindowCloseInterceptorupdated for multi-window: closing a window when others are open closes it directly; closing the last window disconnects and shows Home (preserves single-window UX).AppStateownership moved from the app-levelDataBridgeAppscene to per-windowMainViewvia@StateObject, with@FocusedObjectrouting menu commands to the active window.
- Completely redesigned
BackupRestorePanelto mirror the TablePlus backup dialog:- Left column: saved connection list (grouped, searchable, with DB type badges)
- Center column: database list (auto-loaded via
listDatabases()when a connection is selected; works even when not currently connected — creates a temporary adapter) - Right column: format picker (
--format=custom,--format=sql,--format=tar), content selector (full / schema-only / data-only), compress toggle, or file browser for restore - Footer: "Start backup…" / "Start restore…" button with inline result (success/fail, file size, duration)
- File name header shows the selected database name as default backup filename.
- Auto-selects the active connection and database when opened from the sidebar while connected.
- Added "Backup database…" and "Restore database…" action buttons to the left branding panel on the Home screen, above "New Connection" / "New Group" (matches TablePlus layout).
- Opens the full 3-column wizard panel — user can pick any saved connection + database even when not connected.
- Picker tag mismatch warning —
selectedFormatwas initialized as.custombut SQLite/MySQL only support.sql, causing SwiftUIPicker: the selection "custom" is invalidconsole errors. Format is now initialized to.sql(universal) and reset to the first available format whenever the selected connection changes.
- New ER Diagram tab type renders all tables and foreign-key relationships as an interactive graph using CoreGraphics/AppKit custom
NSView. - Table cards display column names, abbreviated data types, PK/FK badges, and NOT NULL indicators.
- Relationship lines drawn as bezier curves with arrowheads and cardinality markers (1 / N).
- Drag any table card to rearrange the layout.
- Zoom: Cmd+scroll, trackpad pinch, or toolbar +/− buttons.
- Pan: hold Space + drag, or drag on empty canvas area.
- Auto layout: Sugiyama-lite BFS layering based on FK relationships.
- Fit to view: scales and centers the diagram to fill the viewport.
- Double-click a table card to open its data grid tab.
- Hover highlight on table cards; selected card gets accent border.
- Dot-grid background pattern adapts to light/dark mode.
- Open via right-click Tables group → "ER Diagram" in the sidebar.
- New
BackupService(Services/Export/BackupService.swift) — actor-based service that wraps native CLI tools:- PostgreSQL:
pg_dump/pg_restore/psql— supports Custom (compressed), Plain SQL, and Tar formats. - MySQL:
mysqldump/mysql— SQL format. - SQLite: direct file copy for backup, file replace for restore.
- PostgreSQL:
- Automatic tool discovery across Homebrew (Intel + ARM), Postgres.app, and system paths; falls back to
which. - New
BackupRestorePanel— nativeNSPanelcentered over the main window:- Backup: format picker, compress toggle, content selector (full / schema-only / data-only),
NSSavePanelfor output path, inline result (file size, duration, errors). - Restore: file browser with auto-format detection by extension, danger warning before overwrite, inline result with sidebar refresh on success.
- Connection header shows name, host, database, and DB type badge.
- Backup: format picker, compress toggle, content selector (full / schema-only / data-only),
- Sidebar bottom bar now has two new icon buttons: ↑ Backup and ↓ Restore.
- Warning cleanup — Cleared all compiler warnings flagged by Xcode (except the deprecated
SecKeychain*APIs inKeychainService, which need a proper migration to the data-protection keychain):SQLiteAdapter.bind— discard the result ofwithUnsafeByteswith_ =AppState.openTableStructure— removed danglingexisting.idexpression that did nothingAppState.switchDatabase— discard the result of the MySQLUSEexecuteRawcall with_ =SQLContextParser.tableBeforeParen— removed unusedschemaPoslocalSQLFormatter.format—var clauseIndent→let; removed the unusedinSelectList,inGroupOrderBy, and JOIN-loopulocals that were written but never readExportTableSheet.performExport— droppedawaitin front ofshowSavePanel/showDirectoryPanel(both are synchronousNSSavePanel/NSOpenPanelwrappers)
- New
ImportSQLWizard.swift— opens after picking a.sqlfile via Import → From SQL Dump… in the Tables context menu. - Shows file metadata (name, size, encoding picker) and a scrollable preview of the first 100,000 characters before hitting the database.
- Import result is rendered inline in the wizard (success/total count + first error) instead of via a separate alert.
- Sidebar is refreshed when the wizard closes (not during execution) so newly-imported tables appear without re-rendering the parent mid-import.
AppState.connect(config:password:)now races the connection attempt against a 10-second timer viawithThrowingTaskGroup. If the driver blocks (unreachable host, dropped packets, firewall) the UI no longer stays stuck on "Connecting…" forever — the user gets an error and can retry.
- Connection group headers are now rendered as minimal uppercase section labels (similar to Finder sidebar / Xcode navigator) instead of the previous rounded "folder" row.
- Inline
+button on each group header opens the New Connection form for that group. - Group count is displayed as a quiet monospaced-digit number next to the label.
- Context menu (New / Rename / Sort / Delete) and drag-and-drop drop target behavior are preserved.
- Clicking a saved connection on Home now loads the keychain password on a detached task so the main thread isn't blocked by a keychain prompt.
- If the user denies/cancels the keychain prompt (or there's no stored password), the connect flow silently aborts instead of attempting to connect with an empty password.
- SQLite connections skip the keychain lookup entirely (SQLite has no password).
- App quit when closing the Connection form panel — The global
WindowCloseInterceptor(installed on every window that became key) was also being attached toConnectionFormPanel, anNSPanel. Clicking its red ✕ posted.windowCloseRequested, whichMainViewinterpreted as "Home → quit" and calledNSApplication.shared.terminate(nil). The interceptor is now only attached to the main content window —NSPanelinstances and any window wherecanBecomeMain == falseare skipped. Closing the connection form now just dismisses the panel. - Connection form panel appeared in the top-right corner of the screen —
panel.center()was called before the hosting controller had laid out, so the panel was positioned using the initial 580×520 contentRect and then drifted when its real size was applied. The panel is now centered overNSApp.mainWindowafter callinglayoutIfNeeded(), so it appears exactly in the middle of the app window.
- Import → From CSV… — Opens
ImportCSVSheetwith:- File picker for
.csvfiles - Delimiter picker (
,;\t|) - First-row-as-header toggle
- Auto column mapping — CSV header columns are matched to table columns by name; user can override or skip via per-column Picker
- Live preview of first 5 rows
- Generates
INSERT INTO … VALUES …for each row with proper quoting and escaping - Shows imported row count / errors
- File picker for
- Import → From SQL Dump… — Reads an
.sqlfile, splits into statements, and executes each one against the active connection. Shows a result alert with success/fail counts and the first error message (if any).
New ExportService.exportTableSQL(description:rows:databaseType:databaseName:to:) emits:
- Header comment block with database name, table name, generation timestamp
DROP TABLE IF EXISTS "schema"."table"CREATE SEQUENCE IF NOT EXISTS …— auto-detected from columns whose default containsnextval(…)CREATE TABLEwith full column definitions (types,NOT NULL,DEFAULT), primary key constraint- Multi-row
INSERT— single statement for all rows, much smaller and faster than per-row statements - Type-aware value formatting: numerics unquoted, booleans as
true/false, strings/dates quoted & escaped CREATE INDEXfor all non-primary-key indices, includingUSING method,INCLUDE (…), and partialWHEREconditions (PostgreSQL)
The old exportSQL(data:table:to:) is kept for backward compatibility but ExportTableSheet now always routes SQL exports through the new method (calls describeTable first to fetch the structure).
SidebarItemRow.splitSQLStatements(_:)— Full state-machine tokenizer that splits a SQL dump correctly:- Skips
;inside single-quoted strings (handles''escape) - Skips
;inside double-quoted identifiers - Skips
;inside--line comments and/* */block comments
- Skips
- Replaces the previous naive
components(separatedBy: ";")approach that would mis-split dumps containing strings with semicolons or timestamps
- SQL export was not re-importable — Previously
exportSQLemitted onlyINSERTstatements without any schema. Importing the file into an empty database failed withrelation "table" does not exist. The new export includes DROP/CREATE/sequences/indices so the file can recreate the table exactly.
- Removed noisy
[Structure]debugprintstatements fromPostgreSQLAdapter.describeTable
- Columns table now has the same 8 columns as the Structure editor: #, column_name, data_type, is_nullable, check, foreign_key, column_default, comment. The standalone
PKcheckbox column was removed; toggle primary key via right-click context menu ("Set as Primary Key" / "Unset Primary Key"). - Foreign key editor popover — click the
foreign_keycell to open a popover with Referenced Table picker (loads from current schema) and Referenced Column field. GeneratesFOREIGN KEY (col) REFERENCES table(col)in the CREATE TABLE. - Default value editor popover — click the
column_defaultcell to open a tabbed popover (String / Expression) with quick-pick buttons for CURRENT_TIMESTAMP, NOW(), gen_random_uuid(), true, false. - CHECK constraint editable inline in the check column, emitted as
CHECK (expr)in CREATE TABLE. - Indexes table now has the same 7 columns as Structure: index_name, index_algorithm, is_unique, column_name, condition, include, comment. Supports partial indexes (
WHERE) and INCLUDE columns (PostgreSQL). - Inline "+ New column" / "+ New index" buttons below each table (matches Structure editor style). The toolbar no longer has these buttons; toolbar now only shows table name input, schema label, and Create button.
- Default table name is pre-filled as
new_tableso the Create button is enabled immediately. - First column auto-selected on view appear so it's immediately editable.
- Auto-close Create Table tab after successful creation.
- Delete table popup with two options (matches TablePlus):
- ☐ Ignore foreign key checks — MySQL
SET FOREIGN_KEY_CHECKS = 0around the DROP. - ☐ Cascade — PostgreSQL
DROP TABLE … CASCADE.
- ☐ Ignore foreign key checks — MySQL
- Tables are not dropped immediately — clicking OK marks the table for deletion. The sidebar row shows a red background and strikethrough text so the user can see what's pending.
- Undo Delete — right-click a pending table to clear the mark.
- Commit via toolbar ✓ button — the existing Commit Changes toolbar button (
checkmark.circle) now also commits pending table deletions by observing.commitChangesin the sidebar. RunsDROP TABLE IF EXISTSfor each marked table, applies cascade/ignore-FK flags per-table, closes any open tabs referencing dropped tables, and refreshes the sidebar. AppState.pendingTableDeletions[String: PendingTableDeletion]holds the marked tables with their per-table options.DeleteTableSheet.swift— new SwiftUI sheet for the delete confirmation.
- Inline "+" button on Tables group in the sidebar — click to open a new Create Table tab directly (tooltip: "New Table"). Makes the action discoverable without needing to right-click.
- "New Table…" in table context menus — Right-clicking a table now shows "New Table…" between "Open structure" and "Copy name". Right-clicking the "Tables" group row also shows "New Table…" and "Open Table List".
- Sidebar bottom bar
+and trash buttons — Replaced by the inline "+" button on the Tables group and the context menu actions. Bottom bar now only shows the schema picker.
- Window auto-resize on launch / connect:
- On app launch the main window shrinks to its compact minimum (900×500, centered) so the connection picker feels like a focused starting screen
- When
activeConnectionIdbecomes non-nil (user connects to a database), the window automatically zooms to fill the available screen viaNSWindow.zoom(nil)so the user gets full workspace immediately
- Persistent details panel width — Width is now stored in
AppState.detailsPanelWidth(default 320pt) and survives toggling the panel off and on. Previously every toggle reset the width to the SwiftUI ideal value. - Custom
ResizeHandle(NSViewRepresentable) — Draggable vertical divider for the details panel, backed by anNSViewwith nativemouseDown/mouseDraggedtracking. Avoids the SwiftUIDragGesturere-layout storm that caused jitter during drag. Cursor hit area is 7pt wide for easier grabbing; the visible divider is 1pt.
- Details panel max width — Capped at 50% of the main screen (was a fixed 420pt). Minimum remains 220pt.
- No animations on panel toggles — Removed
withAnimation { ... }wrappers and the.animation(.easeInOut, value:)modifier from sidebar/details/AI panel toggles. Panels now open and close instantly with a.transaction { $0.disablesAnimations = true }on the workspace root. Applies to: toolbar toggle buttons, Cmd+\ (Toggle Sidebar), Cmd+Shift+A (Toggle AI Panel), and the details panel toggle. - Replaced
HSplitViewwith fixed-width layout for the details panel.HSplitViewtracks its own width and resets it whenever the view is recreated; a plainHStack+ResizeHandle+ explicit.frame(width: detailsPanelWidth)gives us full control over persistence. - Default window size — Changed
.defaultSize(width: 1200, height: 700)to.defaultSize(width: 900, height: 500)to match the new compact launch experience.
- Sidebar Query History tab — Persistent SQL history via SwiftData:
- Records ONLY queries run from the SQL editor (not data grid loads, structure inspections, or internal DML)
- Grouped by day with sticky headers (Today, Yesterday, weekday name, date)
- Row layout: status dot (green/red) · timestamp · duration (ms/s) · row count · favorite star
- 3-line SQL preview with keyword syntax highlighting (SELECT, FROM, WHERE, JOIN… rendered bold purple)
- Error message rendered in red beneath the SQL for failed queries
- Hover actions: inline Copy / Paste buttons appear on mouse-over
- Double-click a row to paste the SQL into the active query editor tab
- Context menu: Paste to Editor, Copy SQL, Favorite/Unfavorite, Delete
- Search bar filters by SQL text (server-side via SwiftData predicate)
- Footer: entry count + Clear button (scoped to active connection)
- Empty state with helpful hint
AppState.recordQueryHistory— Dedicated method that persists a query to the SwiftData history store, separate from the in-memorylogQueryused by the bottom SQL log panelAppState.queryHistoryVersion—@Publishedcounter that increments when a history entry is saved, observed byQueryHistoryTabto trigger reload
- Query history was never saved —
SwiftDataQueryHistoryRepositoryexisted and was registered in DI but was only called fromQueryEngine, whichQueryEditorViewandDataGridViewbypass.QueryEditorView.executeQueryandexplainQuerynow callrecordQueryHistoryon both success and failure paths.
- SQL Formatter —
FormatandMinifybuttons next toRunin the query editor:- Format (Ctrl+Shift+F): beautifies SQL with clause keywords on their own line, arguments indented by one tab. SELECT columns split onto separate lines, JOINs aligned at base indent with target + ON inline, nested function calls and string literals preserved.
- Minify: collapses SQL onto a single line with single-space separators (strings preserved).
SQLFormatter.swiftwith custom tokenizer aware of strings, comments, and multi-char operators.
- Query editor text persistence — Switching tabs no longer loses the query being written.
AppState.queryEditorText[tabId]caches text per-tab; restored ononAppear, saved ononChange. - Run statement at cursor — Clicking Run (or ⌘R) now executes the single SQL statement that contains the cursor, not the entire editor contents. Splits on
;while ignoring semicolons inside strings and comments.SQLEditorView.Coordinatorforwards cursor position viatextViewDidChangeSelection. - AppKitDataGrid for query results — Query editor results now render in the same
AppKitDataGrid(NSTableView-backed) used for table data, with column resizing, row selection, copy row/cell context menu, and alternating row backgrounds.
- PostgreSQL error messages — Queries that fail with
PSQLErrorpreviously surfaced as the opaque "PostgresNIO.PSQLError error 1.". The adapter now extractsmessage,detail,hint,position, andsqlStatefromPSQLError.serverInfoand re-throws asDataBridgeError.queryExecutionFailedwith a human-readable message.
- SQL Autocomplete Engine — Professional inline completion popup for the query editor:
- Fuzzy matching with scoring: exact match (1000) > exact prefix (500) > word-boundary prefix (200) > fuzzy subsequence (1-50)
- Ranking: primary SQL keywords boosted, recently-used items tracked, type priority (keyword > table > column > function > join)
- Search across all schema — keywords, tables, columns (from every table), 40+ SQL functions with signature hints
- Fuzzy match highlighting — matched characters rendered in bold + accent color
- Match types with icons:
kkeyword,Ttable,Ccolumn,ffunction,Jjoin - Debounced typing (80ms) with instant dismiss on space/semicolon/newline
- Dot-trigger — typing
alias.still works for column completion - Manual trigger via Ctrl+Space
CompletionWindow— Custom NSPanel popup with NSStackView-based row layout:- Fixed window size (420pt wide, max 10 items)
- Pure NSView frame layout (no NSScrollView/NSTableView) — eliminates horizontal scroll/overflow issues
- Visual effect background with rounded corners
- Arrow key navigation, Enter/Tab to insert, Escape to dismiss
- Screen-edge clamping (never overflows visible screen)
SQLContextParser— Lightweight SQL tokenizer for cursor context detection (not used in current "search-all" mode but kept for future context-aware suggestions)- App Icon (
.appbundle) —dev.shnow wraps the SPM executable into a proper.appbundle with Info.plist + AppIcon.icns so Finder/Dock show the DataBridge logo instead of the generic "exec" icon CFBundleIconFile=AppIconadded to Info.plist
- Bundle the app — Running the raw SPM executable showed the generic exec icon;
dev.shnow builds.appbundle with icon + resources - Completion popup window level — Changed from
.popUpMenuto.floatingwithhidesOnDeactivate=trueso the popup no longer overlays other apps when DataBridge loses focus - Fatal error: "Can't take a prefix of negative length" — Clamped
cursorOffsetandprefixLengthin completion coordinator to prevent crash when cursor is at position 0
- Replaced
AppIcon.icnsand all Assets icon sizes with new logo logo.pngupdated
- Table Context Menu — Right-click tables in sidebar with full action menu:
- Open in new tab / Open structure
- Copy name
- Export... (CSV/JSON/SQL with multi-table selection, configurable options)
- Copy Script As (CREATE TABLE, SELECT, INSERT, UPDATE, DELETE)
- Truncate... / Delete... with confirmation dialogs
- Export Dialog — Split-panel export UI:
- Left panel: table list with checkboxes (multi-select, default = right-clicked table)
- Right panel: CSV/JSON/SQL format tabs with options (delimiter, quoting, line break, decimal, NULL handling, field names header)
- Single table export via NSSavePanel, multi-table via folder picker
- New Database Dialog — Create new database from Cmd+K switcher (Name, Encoding, Collation)
- Open Structure — Context menu +
AppState.openTableStructure()opens table directly in Structure mode - Pointer Cursor —
ClickableModifier.swiftwith.pointerCursor()and.clickable()modifiers applied across all interactive elements (sidebar items, tab bar, bottom bar buttons, structure editor, filter bar) - Structure Editor — Auto-track on deselect — Editing a column/index and clicking away now auto-detects changes (name, type, nullable, default, comment) without requiring Enter
- Structure Editor — Inline add buttons — "+ New column" / "+ New index" buttons below each table for quick creation
- Structure Editor — Bottom bar buttons —
+ Columnand+ Indexmoved from toolbar to bottom bar next to Structure tab
ContentTabnow includesinitialViewModefield for opening tabs in specific modeBottomTabBaracceptsshowAddIndexbinding andonAddColumncallback+ Indexform changed from popover to sheet for consistent positioning- Export menu item added to Database menu in menu bar
- "Open in new tab" no longer creates duplicate tabs (reuses existing via
openTable()) - Structure editor column cell views extracted into
@ViewBuilderfunctions to prevent horizontal scroll on row select
- Create Table view — Full table creation UI with columns (name, type, PK, nullable, default, comment) and indexes (name, columns, unique, algorithm)
- SQL Log toggle — Bottom bar button to show/hide query log panel
- Structure editor UX — Prevent horizontal scroll when selecting rows by extracting cell views and controlling
@FocusState
- Data type arrays changed from private to internal for reuse in CreateTableView
- Row color coding — New rows (green), deleted rows (red), edited rows (orange) with full-row NSTableRowView backgrounds
- Column default editor — Tabbed popover with String/Expression/Sequence tabs
- Mark-for-deletion — Dropped columns/indexes stay visible with red background and strikethrough, context menu shows "Undo Drop"
- RowBackgroundSetter — NSViewRepresentable that walks NSView hierarchy to set NSTableRowView.backgroundColor
- NOT NULL column creation auto-adds DEFAULT based on data type (int→0, bool→false, timestamp→CURRENT_TIMESTAMP, etc.)
- Duplicate column prevention with
IF NOT EXISTS - TextField height increased with
.squareBorderstyle - Background colors clear after Apply via table reload
TableDescription,IndexInfo,ForeignKeyInfo,ConstraintInfonow conform toEquatablelistForeignKeys/listAllConstraintsreturn CHECK constraint definitions (all adapters)
- Editable Structure view — Inline editing of columns (rename, type, nullable, default, comment) and indexes
- Foreign Key editor — Popover for creating/editing FK constraints with referenced table/column pickers
- Index management — Add/modify/drop indexes with algorithm, unique, condition, include, comment support
- Structure change tracking —
StructureChangeenum with SQL generation for ALTER TABLE operations - Commit preview — Review generated SQL before applying structure changes
- DataGrid performance — NSTableView-backed grid with virtualized rendering
- FK/Enum/Boolean UI — Clickable foreign key links, enum dropdowns, boolean toggles in data cells
- Function editor — View and edit database functions
- Dark mode — Full dark mode support
- Initial release — macOS (Swift/AppKit) and Windows (C++/WinUI 3)
- Connection management (PostgreSQL, MySQL, SQLite) with SSH tunnel support
- Database browser with sidebar tree (schemas, tables, views, functions)
- Data grid with pagination, sorting, filtering
- Query editor with syntax highlighting
- AI chat integration (Anthropic/OpenAI/Ollama)