feat(explain): rebuild the query plan viewer end to end - #2117
Merged
Conversation
# Conflicts: # TablePro/ViewModels/ERDiagramViewModel.swift # TablePro/Views/ERDiagram/ERDiagramView.swift # TablePro/Views/QueryPlan/QueryPlanDiagramView.swift
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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
Rebuilds the EXPLAIN query plan feature end to end: how a plan is produced, how it is parsed, and how it is shown.
Builds on #2112 and includes its two commits, so @sophiathedev's MySQL TREE parsing and trackpad zoom are preserved with their authorship intact. #2112 can close in favour of this.
Why
The feature had three structural problems, each producing a cluster of symptoms.
Two execution paths that disagreed. The toolbar called
runVariantExplaindirectly, which never consultedExecutionGateProvider. Safe Mode confirmation was therefore skipped on every database that declares an EXPLAIN variant, andEXPLAIN ANALYZEruns the query. That path also had no stale-result fencing, never stored its task so Stop could not cancel it, wrote errors into the plan text field as"Error: ...", and never recorded to Query History.Plan format was inferred from
DatabaseType, but it is a property of the variant.ExplainVariantcarried no format, soQueryPlanParserFactoryguessed from a hardcoded switch. PGlite was handed PostgreSQL'sEXPLAIN (FORMAT JSON)by the registry merge but had no parser case, so valid JSON rendered as raw text. Cloudflare D1 and libSQL emit exactly SQLite'sEXPLAIN QUERY PLANand were raw-only. A registry-only plugin could never contribute a parser at all.QueryEditorViewalso dispatched behaviour offvariant.label, a localizable display string.The presentation layer opted out of the app's own patterns. A fixed 180pt detail pane where
TriggerDetailViewusesAutosavingSplitView; severity colours duplicated in two files with noaccessibilityDifferentiateWithoutColorgate; about twenty unlocalized labels; no context menu; no diagram export; no empty or parse-failure state, so a nil plan fell through to a blank rectangle; and no accessibility identifiers, so no UI automation was possible.What changed
PluginKit (additive, no version bump). New
ExplainPlanFormat, a string-based struct likeDatabaseType, so a new format never needs a PluginKit release.ExplainVariantgainsformatthrough a new four-argument initializer; the three-argument one is kept byte for byte and marked@_disfavoredOverload, the same shape as thePluginQueryResult.columnMetafix.Parsing.
QueryPlanParserFactorybecomesExplainPlanParserRegistry, keyed by format. Format resolves as variant-declared, then a curated per-DatabaseTypedefault, then plain text, so already-installed Cloudflare D1 and libSQL plugins are fixed on app update with no plugin release.Execution.
runExplainQuery,runVariantExplainandrunClickHouseExplaincollapse into one gatedrunExplain(variant:)with shared statement extraction, authorization on every path,claim/isCurrent/settlefencing, a cancellable task, errors to the error banner, and history recording.ClickHouseExplainVariantis deleted.Views. A shared
MagnifiableCanvasViewoverNSScrollView.allowsMagnificationgives both diagrams anchored pinch zoom, smart magnify, Cmd and scroll, real scrollers and elastic scrolling from AppKit. The ER diagram adopts it, deletingERDiagramCanvasContainerandERDiagramScrollTranslator. The plan tree becomes anNSOutlineViewwith labelled, resizable, autosaved columns and sorting that reorders siblings within each parent rather than flattening the tree. A plan is now aResultSet, so it rides the existing result tab strip and can be pinned.Also fixes
QuerySplitView, which never setsizingOptions = []on its hosting controllers. That is the divider-pinning trap in CLAUDE.md, latent until something wide was hosted in the results pane.User-visible changes
EXPLAIN ANALYZE.EXPLAIN QUERY PLANreaches the viewer when typed by hand. MySQL's tabularEXPLAINstill stays in the grid.Verification
xcodebuild buildclean, andAllPluginsbuilds all 30 plugins, which is the only target covering the registry-only Cloudflare D1 and libSQL changes.swiftlint lint --strict: 0 violations across 1,392 files.nmon both frameworks: no symbol present on main is missing here, and the old three-argument initializer survives unchanged. NocurrentPluginKitVersionbump and no plugin re-release.UI automation is included but could not be executed locally: the runner reports
Timed out while enabling automation mode, and the pre-existingResultTabPinUITestsfails identically, so this is an environment limitation rather than the new tests.