Skip to content

Commit 347c013

Browse files
committed
fix(datagrid): open materialized views read-only in the data grid
1 parent 9389e84 commit 347c013

20 files changed

Lines changed: 377 additions & 60 deletions

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6060
- Middle-dot separators dropped from the CSV inspector's status bar and the query history rows.
6161
- Connection marked with a tinted symbol rather than a color dot in the query history rows.
6262
- Safe Mode list offering only the levels a connection allows, with the reason under it and in the toolbar tooltip.
63+
- ClickHouse materialized views read-only in the data grid, as on every other engine.
6364

6465
### Removed
6566

@@ -87,6 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8788
- **File > Import > Import Data…** importing every file as SQL. (#3047)
8889
- A file the import panel dimmed still opening, and reaching the wrong importer.
8990
- Materialized view opened from Open Quickly edited as a plain view. (#2522)
91+
- Materialized view rows editable in the data grid, then refused at Save.
9092
- Index edits refused on a PGlite materialized view.
9193
- Structure grid and inspector taking edits the object or engine refuses, such as a materialized view's Type.
9294
- **Delete** and **Duplicate** in a structure row's menu doing nothing on an object that refuses them.

‎TablePro/Core/Coordinators/RowEditingCoordinator.swift‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ final class RowEditingCoordinator: ObservableObject {
4242
// MARK: - Row Operations
4343

4444
func addNewRow() {
45-
guard !parent.safeModeLevel.blocksAllWrites,
45+
guard parent.canEditActiveResult,
4646
let (tab, tabIndex) = parent.tabManager.selectedTabAndIndex,
47-
tab.tableContext.isEditable,
4847
tab.tableContext.tableName != nil else { return }
4948

5049
let tabId = tab.id
@@ -69,9 +68,8 @@ final class RowEditingCoordinator: ObservableObject {
6968
}
7069

7170
func deleteSelectedRows(indices: Set<Int>) {
72-
guard !parent.safeModeLevel.blocksAllWrites,
71+
guard parent.canEditActiveResult,
7372
let (tab, tabIndex) = parent.tabManager.selectedTabAndIndex,
74-
tab.tableContext.isEditable,
7573
!indices.isEmpty else { return }
7674

7775
let tabId = tab.id
@@ -113,9 +111,8 @@ final class RowEditingCoordinator: ObservableObject {
113111
}
114112

115113
func duplicateSelectedRow(index: Int) {
116-
guard !parent.safeModeLevel.blocksAllWrites,
114+
guard parent.canEditActiveResult,
117115
let (tab, tabIndex) = parent.tabManager.selectedTabAndIndex,
118-
tab.tableContext.isEditable,
119116
tab.tableContext.tableName != nil else { return }
120117

121118
let tabId = tab.id
@@ -337,7 +334,7 @@ final class RowEditingCoordinator: ObservableObject {
337334
}
338335

339336
func pasteRows() {
340-
guard !parent.safeModeLevel.blocksAllWrites,
337+
guard parent.canEditActiveResult,
341338
let (tab, tabIndex) = parent.tabManager.selectedTabAndIndex,
342339
tab.tabType == .table else { return }
343340

‎TablePro/Models/Database/StructureEditEligibility.swift‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,9 @@ enum StructureEditAvailability: Sendable, Equatable {
142142
///
143143
/// Pure, so the rule is testable without a connection, and ordered: an engine that cannot edit
144144
/// structure at all says that first, then the object's kind, then the engine's own statement for the
145-
/// operation. Reading the kind as one `isView` Bool is the defect this replaces, because
146-
/// `TableInfo.TableType.allowsRowEditing` is true for a materialized view, so the Structure tab
147-
/// offered `ADD COLUMN`, `SET NOT NULL`, type changes and constraint edits that PostgreSQL always
148-
/// refuses. (#2726)
145+
/// operation. Reading the kind as one `isView` Bool is the defect this replaces, because the Bool
146+
/// read false for a materialized view, so the Structure tab offered `ADD COLUMN`, `SET NOT NULL`,
147+
/// type changes and constraint edits that PostgreSQL always refuses. (#2726)
149148
enum StructureEditEligibility {
150149
static func allows(
151150
_ operation: StructureEditOperation,

‎TablePro/Models/Query/EditorTabPayload.swift‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ internal struct EditorTabPayload: Codable, Hashable {
3838
/// Whether this tab displays a database view (read-only)
3939
internal let isView: Bool
4040
/// The object's own kind, which decides which structure edits the tab may offer. Carried beside
41-
/// `isView` because that Bool answers a different question and cannot tell a materialized view
42-
/// from a table. (#2726)
41+
/// `isView` because that Bool cannot say which of seven kinds the object is. (#2726)
4342
internal let objectType: TableInfo.TableType?
4443
/// Whether to show the structure view instead of data (for "Show Structure" context menu)
4544
internal let showStructure: Bool

‎TablePro/Models/Query/QueryResult.swift‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ struct TableInfo: Identifiable, Hashable, Sendable {
170170
/// that the server always refuses.
171171
var allowsRowEditing: Bool {
172172
switch self {
173-
case .view, .externalTable, .sequence:
173+
case .view, .materializedView, .externalTable, .sequence:
174174
return false
175-
case .table, .materializedView, .foreignTable, .systemTable, .partitionedTable:
175+
case .table, .foreignTable, .systemTable, .partitionedTable:
176176
return true
177177
}
178178
}

‎TablePro/Models/Query/QueryTabState.swift‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,11 +574,10 @@ struct TabTableContext: Equatable {
574574

575575
/// The object's own kind, carried beside `isView` rather than replacing it.
576576
///
577-
/// The two answer different questions. `isView` decides whether the *rows* may be written, which
578-
/// a dozen Bool-only carriers already speak (deeplinks, the URL parser, scripting, recents), and
579-
/// it comes from `allowsRowEditing`, which is deliberately true for a materialized view because
580-
/// a matview does hold rows. This says which of seven kinds the object is, which is the only
581-
/// thing that can say which *structure* edits it accepts. Conflating them is the defect. (#2726)
577+
/// `isView` is a read-only mark that a dozen Bool-only carriers already speak (deeplinks, the URL
578+
/// parser, scripting, recents), and a tab saved by an older build can carry it false over a
579+
/// materialized view. It cannot say which of seven kinds the object is. This can, and only the
580+
/// kind says which *structure* edits the object accepts. (#2726)
582581
///
583582
/// Nil on a tab restored from a file written before this existed, and on any path that never
584583
/// learned the kind; `resolvedObjectKind()` falls back to what `isView` can still tell us.
@@ -588,6 +587,10 @@ struct TabTableContext: Equatable {
588587
objectType ?? (isView ? .view : .table)
589588
}
590589

590+
var allowsRowEditing: Bool {
591+
!isView && resolvedObjectKind().allowsRowEditing
592+
}
593+
591594
var primaryKeyColumn: String? { primaryKeyColumns.first }
592595

593596
/// A tab opened without an explicit database carries an empty name and follows the window's

‎TablePro/Models/Schema/ForeignKeyEditSupport.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ enum ForeignKeyEditPolicy {
6868
/// - Parameter kindRefusal: Why the object's own kind refuses a foreign key edit, nil when it
6969
/// accepts one. Supplied by `StructureEditEligibility`, because only the per-kind matrix knows
7070
/// which of seven object kinds is in front of the user. This used to be an `isTable` Bool
71-
/// derived from `allowsRowEditing`, which is true for a materialized view, so the "+" was
71+
/// derived from `allowsRowEditing`, which was true for a materialized view, so the "+" was
7272
/// offered over an `ADD CONSTRAINT` PostgreSQL always refuses. (#2726)
7373
static func resolve(
7474
support: ForeignKeyEditSupport,

‎TablePro/Views/Main/Child/DataTabGridDelegate.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ final class DataTabGridDelegate: DataGridViewDelegate {
115115
}
116116

117117
func dataGridEmptySpaceMenu() -> NSMenu? {
118-
guard let onAddRow else { return nil }
118+
guard let onAddRow, coordinator?.canAddRow == true else { return nil }
119119
let menu = NSMenu()
120120
let target = StructureMenuTarget { onAddRow() }
121121
let item = NSMenuItem(

‎TablePro/Views/Main/Child/MainEditorContentView.swift‎

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ struct MainEditorContentView: View {
189189
updateHasQueryText()
190190
cachedChangeManager = AnyChangeManager(changeManager)
191191
wireDataTabDelegateStableRefs()
192-
refreshDataTabDelegateMutableRefs()
193192
coordinator.dataTabDelegate = dataTabDelegate
194193
}
195194
.onDisappear {
@@ -204,36 +203,17 @@ struct MainEditorContentView: View {
204203
.onChange(of: selectionState.indices) { newIndices in
205204
onSelectionChange(newIndices)
206205
}
207-
.onChange(of: tabManager.selectedTab?.tableContext.isEditable) { _ in
208-
refreshDataTabDelegateMutableRefs()
209-
}
210-
.onChange(of: tabManager.selectedTab?.tableContext.isView) { _ in
211-
refreshDataTabDelegateMutableRefs()
212-
}
213-
.onChange(of: tabManager.selectedTab?.tableContext.tableName) { _ in
214-
refreshDataTabDelegateMutableRefs()
215-
}
216-
.onChange(of: coordinator.safeModeLevel) { _ in
217-
refreshDataTabDelegateMutableRefs()
218-
}
219206
}
220207

221208
private func wireDataTabDelegateStableRefs() {
222209
dataTabDelegate.coordinator = coordinator
223210
dataTabDelegate.selectionState = selectionState
224211
dataTabDelegate.onCellEdit = onCellEdit
225212
dataTabDelegate.onSortStateChanged = onSortStateChanged
213+
dataTabDelegate.onAddRow = onAddRow
226214
dataTabDelegate.onFilterColumn = onFilterColumn
227215
}
228216

229-
private func refreshDataTabDelegateMutableRefs() {
230-
dataTabDelegate.onAddRow = currentTabAllowsAddRow ? onAddRow : nil
231-
}
232-
233-
private var currentTabAllowsAddRow: Bool {
234-
coordinator.canAddRow
235-
}
236-
237217
// MARK: - Tab Content
238218

239219
@ViewBuilder

‎TablePro/Views/Main/Extensions/MainContentCoordinator+ResultEditing.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension MainContentCoordinator {
3030
var canEditActiveResult: Bool {
3131
guard let tab = tabManager.selectedTab else { return false }
3232
return tab.tableContext.isEditable
33-
&& !tab.tableContext.isView
33+
&& tab.tableContext.allowsRowEditing
3434
&& !safeModeLevel.blocksAllWrites
3535
&& activeResultEditRefusal == nil
3636
}

0 commit comments

Comments
 (0)