feat(datagrid): show whether a materialized view can be refreshed concurrently, and gate its structure edits by kind - #3063
Merged
Conversation
…currently, and gate its structure edits by kind
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
This was referenced Sep 23, 2026
Merged
…exes # Conflicts: # CHANGELOG.md # TablePro/Resources/Localizable.xcstrings # TablePro/ViewModels/QuickSwitcherViewModel.swift
…exes # Conflicts: # CHANGELOG.md
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A materialized view's Indexes tab now says whether Refresh Materialized View can run concurrently, and the Structure tab stops offering edits a materialized view refuses on the routes that still offered them.
Asks 1 and 2 of #2522 (list a matview's indexes, add and drop them) already worked on PostgreSQL when the view was opened from the sidebar, since #2772 and #2778. What was left:
concurrentRefreshAvailability(since feat(plugins): refresh materialized views, show view DDL and edit object comments (#2726) #2753) was read by the Refresh prompt only..viewrow kind for the icon and opened the tab withisView: trueand noobjectType, so the Structure tab gated it as a plain view and refused Add Index with "A view cannot have indexes."What changed
StructureEditingSessionholds the answer as aMetadataLoadState, asked of the server after every index fetch (first load,Cmd+R, after Save, after Refresh Materialized View) and on remount. It is never derived from the index grid: an index left INVALID by a failedCREATE UNIQUE INDEX CONCURRENTLYlists exactly like a valid one. A failed recheck shows "Couldn't check…" rather than the previous answer, because the index list beside it has already been refetched, and only the latest request may settle the state, so a remount's check that finishes after a post-save check cannot overwrite it.ConcurrentRefreshNoteViewshows it as one secondary line in a bottomsafeAreaInsetof the Indexes tab, mirroring the Filter field's top inset. Nothing shows where the engine has no concurrent refresh.QuickSwitcherItem.tableTypecarries theTableInfo.TableTypethe row was built from, through both same-window arms and the cross-window route (LaunchIntent.openTable(objectType:)intoEditorTabPayload).structureEdits: .postgreSQL.StructureEditGate.locksField(at:on:orderedFields:)is the one lock rule. The grid's per-heading lock, the inspector (newInspectorRowField.isEditable) and the commit path all read it, so no surface takes a keystroke another refuses. A list whose add the object or engine refuses (Indexes on a view, Foreign Keys on a matview, Indexes on an engine withoutCREATE INDEX) is locked whole instead of taking input and dropping it.canDuplicate/canDeletefrom the gate replace one engine-wide Bool.TriggerEditEligibilitywithholds triggers only from the kinds measured to refuse them. It is deliberately not a cell of the edit matrix, which offers an uncurated engine nothing but tables: SQLite, SQL Server and Oracle takeINSTEAD OFtriggers on views.relispopulated, and a view refreshed over an empty result is populated with zero rows. Both strings now say "populated".No PluginKit change, so no kit bump.
Measured on PostgreSQL 17.11
indexListreturns a matview's indexes, expression keys included (norelkindfilter).CREATE TRIGGER: accepted on a table, partitioned table, view (INSTEAD OF) and foreign table; refused on a matview (relation "mv" cannot have triggers) and a system catalog.ALTER COLUMN … TYPE,SET DEFAULT,SET NOT NULLon a matview: all refused.REFRESH MATERIALIZED VIEW CONCURRENTLYon a matview createdWITH NO DATAover an empty query: refused until one plain refresh, then accepted withrelispopulated = tand 0 rows.daily_totals(unique index onday, region) shows "This view can be refreshed concurrently.";region_totals(non-unique index only) shows the unique-index requirement; afterCREATE UNIQUE INDEXon the server and a refresh, the line switched to available; the matview's Triggers tab showed no action bar.Before / After
Before: the same tab with no line under the list, which is what 0.75.0 shows. After:
Tests
MaterializedViewConcurrentRefreshNoteTests,MaterializedViewConcurrentRefreshCheckTests(name, schema and scope reach the driver; other kinds never ask; recheck replaces; a failed recheck reports the failure; an older request finishing last cannot overwrite a newer answer),QuickSwitcherObjectKindTests.StructureEditGateTests(PGlite matrix, trigger rule, the per-tab lock),StructureGridDelegateInspectorTests(a matview's locked fields refuse inspector commits, unlocked ones take them, row menu flags),StructureRowMenuRouteTests,MultiRowEditStateTests.Deliberately not fixed here
A tab opened by name alone (a deeplink, AppleScript, the MCP
open_table_tabtool, or a tab file from before tabs stored their kind) still guesses its kind fromisView, as it did before this change. A first version resolved it fromSchemaService's loaded object list, and two review rounds found five defects in that path: the per-schema cache has no database identity, the$statessubscription can fire beforeloadedScopeis committed, adopting a kind leftisView/isEditableon the guess, dropping a guessed session could lose a half-typed inline edit, and the lookup used the raw tab schema instead of the resolved one. Doing it properly needs a scope-atomic catalog revision first, so it was cut to what the Open Quickly route can carry today. For the same reason, choosing a materialized view in Open Quickly while a kind-less tab on it is already open reuses that tab as it is.Open Quickly still opens a materialized view with
isView: true, which keeps its rows read-only, as before this change. That is the right answer on PostgreSQL, whereUPDATEon a materialized view fails withcannot change materialized view; the sidebar route, which makes those rows editable, is the one that is wrong, and it is reported separately rather than copied here.Fixes #2522