Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Internal: tab session registry binds automatically when a coordinator falls back to creating its own registry, so unit tests no longer trip the filter-state debug assertion
- Connection-only payloads no longer create an empty `Query 1` tab when there is no query, title, or source file to populate it
- Import from Other App: cancelling a macOS keychain prompt now stops the import loop instead of silently continuing through every remaining password. The loading screen has a Cancel button, and an explainer alert before reading passwords sets expectations about the per-item prompts (#1134)
- Structure view: switching between Columns / Indexes / Foreign Keys no longer shows stale data from the previous tab. The data grid now invalidates its display cache on schema change, scopes column widths and row selection per tab, and refresh re-fetches all tabs so badge counts persist (#1110)

## [0.39.1] - 2026-05-08

Expand Down
10 changes: 6 additions & 4 deletions TablePro/Views/Results/DataGridCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,8 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData
tableView.scrollRowToVisible(0)
}

func rebuildColumnMetadataCache(from tableRows: TableRows) {
@discardableResult
func rebuildColumnMetadataCache(from tableRows: TableRows) -> Bool {
var enumSet = Set<Int>()
var fkSet = Set<Int>()
let columns = tableRows.columns
Expand All @@ -517,9 +518,10 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData
fkColumns = fkSet

let nextSchema = ColumnIdentitySchema(columns: columns)
if nextSchema != identitySchema {
identitySchema = nextSchema
}
guard nextSchema != identitySchema else { return false }
identitySchema = nextSchema
displayCache.removeAllObjects()
return true
}

// MARK: - Font Updates
Expand Down
4 changes: 2 additions & 2 deletions TablePro/Views/Results/DataGridView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,10 @@ struct DataGridView: NSViewRepresentable {
let oldColumnCount = coordinator.cachedColumnCount

let structureChanged = oldRowCount != rowDisplayCount || oldColumnCount != columnCount
let needsFullReload = structureChanged

coordinator.updateCache()
coordinator.rebuildColumnMetadataCache(from: latestRows)
let schemaChanged = coordinator.rebuildColumnMetadataCache(from: latestRows)
let needsFullReload = structureChanged || schemaChanged

if oldRowCount == 0, rowDisplayCount > 0 {
let rowH = tableView.rowHeight
Expand Down
30 changes: 20 additions & 10 deletions TablePro/Views/Structure/TableStructureView+DataLoading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ extension TableStructureView {

func loadTabDataIfNeeded(_ tab: StructureTab) async {
guard !loadedTabs.contains(tab) else { return }
await fetchTabData(tab)
}

func fetchTabData(_ tab: StructureTab) async {
guard let driver = DatabaseManager.shared.driver(for: connection.id) else { return }

do {
switch tab {
case .columns:
if columns.isEmpty {
columns = try await driver.fetchColumns(table: tableName)
}
columns = try await driver.fetchColumns(table: tableName)
case .indexes:
indexes = try await driver.fetchIndexes(table: tableName)
case .foreignKeys:
Expand All @@ -77,7 +79,7 @@ extension TableStructureView {
ddlStatement = preamble + "\n" + baseDDL
}
case .parts:
break
return
}
loadedTabs.insert(tab)
} catch {
Expand Down Expand Up @@ -106,6 +108,7 @@ extension TableStructureView {
searchText = ""
structureSortDescriptor = nil
sortState = SortState()
selectedRows = []
displayVersion += 1
Task {
await loadTabDataIfNeeded(new)
Expand Down Expand Up @@ -152,18 +155,25 @@ extension TableStructureView {

if confirmed {
discardChanges()
loadedTabs.removeAll()
await loadColumns()
await loadTabDataIfNeeded(selectedTab)
await reloadAllTabs()
}
}
// If cancelled, do nothing
} else {
Task { @MainActor in
loadedTabs.removeAll()
await loadColumns()
await loadTabDataIfNeeded(selectedTab)
await reloadAllTabs()
}
}
}

private func reloadAllTabs() async {
await loadColumns()
await fetchTabData(.indexes)
if connection.type.supportsForeignKeys {
await fetchTabData(.foreignKeys)
}
if selectedTab == .ddl {
await fetchTabData(.ddl)
}
}
}
11 changes: 9 additions & 2 deletions TablePro/Views/Structure/TableStructureView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct TableStructureView: View {
@State var wrappedChangeManager: AnyChangeManager
@State var selectedRows: Set<Int> = []
@State var sortState = SortState()
@State var structureColumnLayout = ColumnLayoutState()
@State var structureColumnLayouts: [StructureTab: ColumnLayoutState] = [:]
@State var columnLayoutPersister: any ColumnLayoutPersisting = FileColumnLayoutPersister()
@State var actionHandler = StructureViewActionHandler()
@State var gridDelegate: StructureGridDelegate
Expand Down Expand Up @@ -208,6 +208,13 @@ struct TableStructureView: View {
)
}

private func columnLayoutBinding(for tab: StructureTab) -> Binding<ColumnLayoutState> {
Binding(
get: { structureColumnLayouts[tab] ?? ColumnLayoutState() },
set: { structureColumnLayouts[tab] = $0 }
)
}

func updateGridDelegate() {
let provider = makeCurrentProvider()
let canEdit = connection.type.supportsSchemaEditing
Expand Down Expand Up @@ -284,7 +291,7 @@ struct TableStructureView: View {
layoutPersister: columnLayoutPersister,
selectedRowIndices: $selectedRows,
sortState: $sortState,
columnLayout: $structureColumnLayout
columnLayout: columnLayoutBinding(for: selectedTab)
)
.safeAreaInset(edge: .top, spacing: 0) {
VStack(spacing: 0) {
Expand Down
Loading