Skip to content

Releases: datlechin/TablePro

v0.10.0

01 Mar 13:27
5f3e434

Choose a tag to compare

Added

  • Support for multiple independent database connections in separate windows with per-window session isolation
  • MongoDB database support
  • Custom About window with version info and links (Website, GitHub, Documentation)
  • Import database connections from URL/connection string (e.g., postgresql://user:pass@host:5432/db)
  • Release notes in Sparkle update window

Fixed

  • New row (Cmd+I) and duplicated row not appearing in datagrid until manual refresh
  • PostgreSQL SSH tunnel connections failing with "no encryption" due to SSL config not being preserved
  • PostgreSQL SSL sslrootcert passed unconditionally to libpq, causing certificate verification failure even in Required mode

v0.9.3

28 Feb 12:23
fc0cf26

Choose a tag to compare

Fixed

  • PostgreSQL SSH tunnel connections failing with "no encryption" due to SSL config not being preserved

v0.9.2

28 Feb 07:36
9b69ba7

Choose a tag to compare

Fixed

  • Fix app bundle not ad-hoc signed — signing step was unreachable when no dylibs were bundled

v0.9.1

28 Feb 07:21
e1b7ca5

Choose a tag to compare

Fixed

  • Fix Sparkle auto-update failing with "improperly signed" error — release ZIPs now preserve framework symlinks and include proper ad-hoc code signatures

v0.9.0

28 Feb 06:56
535e649

Choose a tag to compare

Added

  • Vim keybindings for SQL editor (Normal/Insert/Visual modes, motions, operators, :w/:q commands) with toggle in Editor Settings
  • ^ and _ motions (first non-blank character) in Vim normal, visual, and operator-pending modes
  • :q command to close current tab in Vim command-line mode
  • PostgreSQL schema switching via ⌘K database switcher (browse and switch between schemas like public, auth, custom schemas)

Changed

  • Convert QueryHistoryStorage and QueryHistoryManager from callback-based async dispatch to native Swift async/await — eliminates double thread hops per history operation
  • Consolidate ExportService @published properties into single state struct — reduces objectWillChange events from 7 per batch to 1
  • Consolidate ImportService @published properties into single state struct — reduces objectWillChange events during SQL import
  • Replace DispatchQueue.main.asyncAfter chains in AppDelegate startup with structured Task-based retry loops
  • Merge 3 identical Combine notification subscriptions in SidebarViewModel into Publishers.Merge3
  • Make AIChatStorage encoder/decoder static — shared across all instances instead of duplicated

Fixed

  • Cell edit showing modified background but displaying original value until save (reloadData during active editing ignored by NSTableView, updateNSView blocked by editedRow guard)
  • Undo on inserted row cell edit not syncing insertedRowData (stale values after undo)
  • Vim Escape key not exiting Insert/Visual mode when autocomplete popup is visible (popup's event monitor consumed the key)
  • Copy (Cmd+C) and Cut (Cmd+X) not working in SQL editor — clipboard retained old value due to CodeEditTextView's copy: silently failing
  • Vim yank/delete operations not syncing to system clipboard (register only stored text internally)
  • Vim word motions (w, b, e) using two-class word boundary detection instead of correct three-class (word chars, punctuation, whitespace)
  • Vim visual mode selection now correctly includes cursor character (inclusive selection matching real Vim behavior)
  • Arrow keys now work in Vim visual/normal mode (mapped to h/j/k/l instead of bypassing the Vim engine)
  • Vim block cursor now follows the moving end of the selection in visual mode instead of staying at the anchor
  • Vim visual mode selection highlight now renders visibly (trigger needsDisplay after programmatic selection)
  • Fix event monitor leaks in SQL editor — deinit now cleans up NSEvent monitors, notification observers, and work items that leaked when CodeEditSourceEditor never called destroy()
  • Fix unbounded memory growth from NativeTabRegistry holding full QueryTab objects (including RowBuffer references) — registry now stores lightweight TabSnapshot structs
  • Fix SortedRowsCache storing full row copies — now stores index permutations only, halving sorted-tab memory
  • Fix schema provider memory leak — shared providers are now reference-counted with 5s grace period removal when all windows for a connection close
  • Fix duplicate schema fetches in InlineSuggestionManager — now shares the coordinator's SQLSchemaProvider instead of maintaining a separate cache
  • Fix background tabs retaining full result data indefinitely — RowBuffer eviction frees memory for inactive tabs (re-fetched on switch back)
  • Fix InMemoryRowProvider bulk cache eviction — now uses proximity-based eviction keeping entries near current scroll position
  • Fix stale tabRowProviders entries when tab IDs change without count changing
  • Fix crash on macOS 14.x caused by _strchrnul symbol not found in libpq.5.dylib — switch libpq and OpenSSL from dynamic Homebrew linking to vendored static libraries built with MACOSX_DEPLOYMENT_TARGET=14.0
  • Fix duplicate tabs and lag when inserting SQL from AI Chat or History panel with multiple window-tabs open — notification handlers now only fire in the key window
  • Fix "Run in New Tab" race condition in History panel — replaced fragile two-notification + 100ms delay pattern with a single atomic notification
  • Fix MainContentCoordinator deinit Task that may never execute — added explicit teardown() method with didTeardown guard and orphaned schema provider purge
  • Fix SQLEditorCoordinator deinit deferring InlineSuggestionManager cleanup to Task — added explicit destroy() lifecycle and didDestroy guard with warning log
  • Fix ExportService while-true batch loops not checking Task.isCancelled — cancelled exports now stop promptly instead of running all remaining batches
  • Fix DataGridView full column reconfiguration on every resultVersion bump — narrowed rebuild condition to only trigger when transitioning from empty state
  • Fix ConnectionHealthMonitor fixed 30s interval that delays failure detection — added checkNow() with wakeUpContinuation for immediate health checks and exponential backoff
  • Fix HistoryPanelView and TableStructureView asyncAfter copy-reset timers not cancellable — replaced with cancellable Task pattern
  • Fix MainContentView redundant onChange handler causing cascading re-renders on tab/table changes
  • Fix DatabaseManager notification observer creating unnecessary Tasks when self is already deallocated — added guard let self before Task creation

v0.8.0

27 Feb 04:44
5fbf214

Choose a tag to compare

New Features

  • Native macOS Window Tabs: The tab bar is now rendered by macOS itself — identical to Finder, Safari, and Xcode tabs with automatic dark/light mode support, drag-to-reorder, and "Merge All Windows" for free
  • Independent Tab Windows: Each tab is a full independent window with its own sidebar, editor, and state — no more shared state between tabs

Improvements

  • Tab Switching Performance: Schema is now cached per connection so new native tabs reuse the already-loaded schema instead of re-fetching from the database (saves 500ms–2s per tab)
  • Background Schema Loading: Schema loads concurrently — table data appears immediately while autocomplete schema loads in the background
  • Sidebar MVVM Refactor: Sidebar table list migrated to a testable SidebarViewModel architecture with extracted TableRowView and context menu components
  • Window title updates dynamically after in-place navigation (sidebar click, FK navigation)
  • 10+ SwiftUI rendering optimizations to prevent O(N) view cascades across windows

Bug Fixes

  • Fixed sidebar losing keyboard focus (arrow key navigation) after opening a second table tab
  • Fixed sidebar active state flash when clicking a table that opens in a new native window tab
  • Fixed sidebar losing active state when opening a second table in a new native window tab
  • Fixed sidebar not refreshing after switching databases via Cmd+K
  • Fixed Cmd+W in empty state doing nothing — now closes the connection window and disconnects
  • Fixed Cmd+K database switch flooding all windows with error alerts
  • Fixed clicking a table in the sidebar replacing the current tab instead of opening a new one
  • Fixed Cmd+W on any tab disconnecting the entire session
  • Fixed Cmd+T from empty state creating two native tabs instead of one
  • Fixed native tab title showing "SQL Query" instead of the table name
  • Fixed Cmd+W on the last tab disconnecting the session instead of returning to empty state

v0.7.0

25 Feb 03:32
85ff3d5

Choose a tag to compare

Added

  • Quick search and filter rows can now be combined — when both are active, their WHERE conditions are joined with AND
  • Foreign key columns now show a navigation arrow icon in each cell — click to open the referenced table filtered by the FK value

Changed

  • Metadata queries (columns, FKs, row count) now run on a dedicated parallel connection, eliminating 200-300ms delay for FK arrows and pagination count on initial table load
  • Approximate row count from database metadata displays instantly with data; exact count refines silently in the background
  • Show warning indicator on filter presets referencing columns not in current table
  • Increase filter row height estimate for better accessibility support
  • FK navigation now uses dedicated FilterStateManager.setFKFilter API instead of direct property manipulation
  • Add syntax highlighting to Import SQL file preview
  • XLSX export now enforces the Excel row limit (1,048,576) per sheet and uses autoreleasepool per row to reduce peak memory during large exports
  • Multiline cell values now use a scrollable overlay editor instead of the constrained field editor, enabling proper vertical scrolling and line navigation during inline editing
  • AnyChangeManager now uses a reference-type box for lazy initialization, avoiding Combine pipeline creation during SwiftUI body evaluation
  • DataGridView identity check moved before AppSettingsManager read to skip settings access when nothing has changed
  • DataGridView async column width write-back now uses an isWritingColumnLayout guard to prevent two-frame bounce
  • Tab switch flushPendingSave debounced to skip redundant saves within 100ms of rapid tab switching
  • SQL editor frame-change notification throttled to 50ms to avoid redundant syntax highlight viewport recalculation on every keystroke
  • SQL editor text binding sync now uses O(1) NSString length pre-check before O(n) full string equality comparison
  • Toolbar executing state now fires a single objectWillChange instead of double-publishing isExecuting and connectionState
  • Row provider onChange handlers coalesced into a single trigger to avoid redundant InMemoryRowProvider rebuilds
  • SQL import now uses file-size estimation instead of a separate counting pass, eliminating the double-parse overhead for large files
  • History cleanup COUNT + DELETE now wrapped in a single transaction to reduce journal flushes
  • SQLite fetchTableMetadata now caps row count scan at 100k rows to avoid full table scans on large tables
  • SQLite fetchIndexes uses table-valued pragma functions in a single query instead of N+1 separate PRAGMA calls
  • MySQL empty-result DESCRIBE fallback now only triggers for SELECT queries, avoiding redundant round-trips for non-SELECT statements
  • Remove redundant String(query) copy in MariaDB query execution
  • MySQL result fetching now uses mysql_use_result (streaming) instead of mysql_store_result (full buffering), so only the capped row count is held in memory instead of the entire server result set
  • Instant pagination via approximate row count — MySQL/PostgreSQL tables now show "~N rows" immediately with data, then refine to exact count in background
  • QueryTab uses value-based equality for SwiftUI diffing, eliminating unnecessary ForEach re-renders on tab array writes
  • Cached static regex for extractTableName, SQLiteDriver.stripLimitOffset, and SQL function expressions to avoid per-call compilation
  • Static NumberFormatter in status bar to avoid per-render locale resolution
  • Batch TableProTabSmart field writes into single array store to avoid 14 CoW copies per query execution
  • Tab persistence writes moved off main thread via Task.detached
  • Single history entry per SQL import instead of per-statement recording
  • WAL mode enabled for query history SQLite database
  • Merged fetchDatabaseMetadata into single query for MySQL and PostgreSQL
  • Health ping now uses dedicated metadata driver to avoid blocking user queries
  • SSH tunnel setup extracted into shared helper to eliminate code duplication
  • PostgreSQL DDL queries restructured with async let for cleaner dispatch (sequential on serial connection queue)
  • Cancel query connection now uses 5-second connect timeout
  • PostgreSQL connection parameters properly escaped for special characters
  • SQLite fetchAllColumns overridden with single sqlite_master + pragma_table_info query
  • Eliminated intermediate [UInt8] buffer in MySQL and PostgreSQL field extraction
  • Column layout sync gated behind user-resize flag to skip O(n) loop on cursor moves
  • Column width calculation uses monospace character arithmetic instead of per-row CoreText calls
  • DataChangeManager maintains change index incrementally instead of full O(n) rebuild
  • JSON export buffers writes per row instead of per field
  • SQLFormatterService uses NSMutableString for keyword uppercasing and integer counter for placeholders
  • SQLContextAnalyzer uses single alternation regex and single-pass state machine for string/comment detection
  • escapeJSONString iterates UTF-8 bytes instead of grapheme clusters
  • AppSettingsStorage caches JSONDecoder/JSONEncoder as stored properties
  • AppSettingsManager stores validated settings in memory after didSet
  • FilterSettingsStorage uses tracked key set instead of loading full plist
  • Keychain saves use SecItemAdd + SecItemUpdate upsert pattern instead of delete + add
  • Autocomplete detectFunctionContext uses index tracking instead of character-by-character string building

Fixed

  • Fix AND/OR filter logic mode ignored in query execution — preview showed correct OR logic but actual query always used AND
  • Fix filter panel state (filters, visibility, quick search, logic mode) not preserved when switching between tabs
  • Fix foreign key navigation filter being wiped when switching to a new tab (tab switch restore overwrote FK filter state)
  • Fix pagination count appearing 200-300ms after data loads — approximate row count from database metadata now displays instantly with data, exact count refines silently in the background
  • Fix foreign key navigation arrows and pagination count appearing with visible delay on initial table load — metadata now fetches on a dedicated parallel connection concurrent with the main query
  • Fix LibPQ parameterized query using Swift deallocate() for strdup-allocated memory instead of free()
  • FTS5 search input now sanitized to prevent parse errors from special characters like *, OR, AND
  • Fix SQL export corrupting newline/tab/backslash characters for PostgreSQL and SQLite (MySQL-style backslash escaping was incorrectly applied to all database types)
  • Fix PostgreSQL SQL export failing to import when types/sequences already exist (DROP IF EXISTS now always emitted for dependent types and sequences)
  • Fix PostgreSQL SQL export missing CREATE TYPE definitions for enum columns, causing import errors
  • Fix PostgreSQL DDL tab not showing enum type definitions used by table columns
  • Fix compilation error for PostgreSQL dependent sequences export (fetchDependentSequences missing from DatabaseDriver protocol)
  • Fix PostgreSQL LIKE/NOT LIKE expressions missing ESCAPE '\' clause, causing wildcard escaping (\%, \_) to be treated as literal characters
  • Fix SQLite regex filter silently degrading to LIKE substring match instead of being excluded from the WHERE clause

v0.6.4

23 Feb 09:01
v0.6.4
82cb109

Choose a tag to compare

Fixed

  • Fix PostgreSQL SQL export failing to fetch DDL for tables (passed quoted identifier instead of raw table name to catalog queries)

v0.6.3

23 Feb 07:47
v0.6.3
d3e5b88

Choose a tag to compare

Changed

  • Extract shared performDirectTabSwitch into MainContentCoordinator to eliminate duplicate tab-switch logic
  • Welcome window now uses native macOS frosted glass translucency (NSVisualEffectView with behind-window blending)

Fixed

  • Auto-detect MySQL vs MariaDB server type from version string to use correct timeout variable (max_execution_time for MySQL, max_statement_time for MariaDB)
  • Improved tab switching performance by caching row providers and change managers across SwiftUI render cycles
  • Eliminated selection sync feedback loop causing redundant DataGridView updates during tab switch
  • Enabled NSTableView row view recycling to reduce heap allocations during scrolling
  • Reduced SwiftUI re-render cascades by batching @published mutations during tab switch
  • Improved DataGrid scrolling performance:
    • Row views now recycled via NSTableView's reuse pool instead of allocating new objects per scroll
    • Replaced O(n) String.count with O(1) NSString.length for large cell value truncation
    • Replaced expensive NSFontDescriptor.symbolicTraits checks with O(1) pointer equality on cached fonts
    • Added layerContentsRedrawPolicy and canDrawSubviewsIntoLayer to reduce compositing overhead
    • Cached NULL display string locally instead of per-cell singleton access
    • Cached AnyChangeManager to avoid per-render allocation with Combine subscriptions
    • Deferred accessibility label generation to when VoiceOver is active
    • Removed unnecessary async dispatch in focusedColumn, collapsed two reloadData calls into one

v0.6.2

23 Feb 06:06
e323f7d

Choose a tag to compare

  • Bug fixes and improvements