Skip to content

Release: dev -> main#292

Merged
erikdarlingdata merged 15 commits intomainfrom
dev
Apr 27, 2026
Merged

Release: dev -> main#292
erikdarlingdata merged 15 commits intomainfrom
dev

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Summary

Release merge from dev to main. 14 PRs since v1.8.0.

Features

Fixes

Maintenance

Docs

Test plan

  • CI green on dev
  • dotnet build -c Release clean
  • dotnet test passes
  • Smoke test desktop app: open a plan, verify minimap and colored links render
  • Tag v1.9.0 after merge

erikdarlingdata and others added 14 commits April 25, 2026 19:25
Dependencies:
- Avalonia 11.3.12 -> 11.3.14 (DataGrid 11.3.13)
- ModelContextProtocol + .AspNetCore 0.7.0-preview.1 -> 1.2.0 (GA)
- Microsoft.NET.Test.Sdk 17.8.0 -> 18.4.0
- xunit 2.5.3 -> 2.9.3, xunit.runner.visualstudio 2.5.3 -> 3.1.5, coverlet.collector 6.0.0 -> 10.0.0
- Meziantou.Framework.Win32.CredentialManager 1.7.17 -> 1.7.18
- ScottPlot 5.1.57 -> 5.1.58, SkiaSharp.NativeAssets.Linux 3.119.0 -> 3.119.2

Closes 3 transitive vulnerabilities: Tmds.DBus.Protocol (GHSA-xrw6-gwf8-vvr9),
System.Net.Http (GHSA-7jgj-8wvc-jh57), System.Text.RegularExpressions (GHSA-cmhx-cq75-c4mj).

Source warnings:
- PlanAnalyzer.cs: null-conditional on StatementText and PhysicalOp
- QueryStoreHistoryWindow.cs: pattern-match ComboBoxItem; replace obsolete DataGridRow.GetIndex with .Index
- WindowsCredentialService: [SupportedOSPlatform("windows5.1.2600")]
- CredentialServiceFactory: switch to OperatingSystem.IsWindows/IsMacOS; pragma at the cross-platform bridge
- CredentialCommand: explicit OperatingSystem.IsWindows guard + pragma

71/71 tests passing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single-package swap. The test project uses only [Fact] + the basic Assert.* surface
(no theories, no fixtures, no IAsyncLifetime, no ITestOutputHelper), all of which
carry over to v3 unchanged. xunit.runner.visualstudio 3.1.5 already supports v3.

71/71 tests passing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
)

SqlClient: API surface unchanged (codebase already uses Encrypt explicitly,
SqlConnectionEncryptOption, async-only patterns). Note: the
Microsoft.Data.SqlClient.Extensions.Azure auxiliary package is only published at 1.0.0
and isn't required for ActiveDirectoryInteractive in 7.0.1 — the auth method remains
in the core package.

ScriptDom: 180.6.0 tracks SQL 2025 grammar. No API surface changes affect
SqlObjectResolver, SqlFormattingService, or SqlFormatSettingsService. Sticking
with TSql160/170 parsers as today; future work can adopt TSql180Parser when needed.

71/71 tests passing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mechanical API rename pass — no architectural changes.

- Option<T>(name, description) -> Option<T>(name, ...aliases) { Description = ..., DefaultValueFactory = _ => v, Required = true }
- AddAlias(x) -> ctor params or Aliases.Add(x)
- IsRequired -> Required
- AddCommand(c) -> Subcommands.Add(c)
- SetHandler(async ctx => ...) / SetHandler(binder, opts...) -> SetAction(...)
- ctx.ParseResult.GetValueForOption/Argument(x) -> parseResult.GetValue(x)
- root.InvokeAsync(args) -> root.Parse(args).InvokeAsync()
- Argument<T>(name, description) ctor gone -> single-arg ctor + Description property

Smoke-tested: --help on root, analyze, credential render correctly with
descriptions/defaults/aliases. Functional analyze on a .sqlplan emits the
expected JSON. 71/71 tests passing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Child namespace has implicit access to the parent — and the using was placed
after the namespace declaration, which is unusual style.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…275)

* Add rule 38 for standard edition limitation on batch mode when dop=2

* Changes Made
1. Fix Rule 38 edge case (PlanAnalyzer.cs)
• Introduced editionKnown variable: !string.IsNullOrEmpty(serverMetadata?.Edition)
• If ServerMetadata is non-null but Edition is null/empty (collection failure), the Info branch now fires instead of silently skipping
2. Wire RunLiveAsync(FileInfo?, string, string?, string?, DirectoryInfo?, bool, string?, bool, int, string, bool, bool, ICredentialService?, string?, string?, AnalyzerConfig) (AnalyzeCommand.cs)
• Fetches ServerMetadata via FetchServerMetadataAsync(string, bool, CancellationToken) after connection is established
• Passes it to PlanAnalyzer.Analyze(plan, analyzerConfig, serverMetadata)
• Non-fatal try/catch so analysis continues if metadata collection fails
3. Wire RunAsync(FileInfo?, bool, string, bool, bool, AnalyzerConfig) (QueryStoreCommand.cs)
• Same pattern: fetches metadata once before the plan analysis loop
• Reuses it for all plans in the batch (same server)
4. Wire McpQueryStoreTools (McpQueryStoreTools.cs)
• Fetches metadata before the LINQ Select that parses plans
• Passes via named parameter serverMetadata: serverMetadata
5. PlanTestHelper overload (PlanTestHelper.cs)
• Added LoadAndAnalyze(string planFileName, ServerMetadata? serverMetadata) overload
• Original overload delegates to the new one with null
6. Unit tests (PlanAnalyzerTests.cs) — 4 tests:
• Standard + DOP=2 + batch + MAXDOP>2 → Warning ✅
• Standard + DOP=2 + batch + MAXDOP=2 → no warning ✅
• No metadata + DOP=2 + batch → Info ✅
• Metadata with null Edition + DOP=2 + batch → Info ✅ (edge case fix)
* PlanViewerControl.axaml.cs — Added:
• A "minimap" toggle button (top-left, always visible over the plan canvas, very small)
• A minimap panel (300×300 default, with close button "✕", header bar, and a Canvas for rendering)
PlanViewerControl.axaml.cs — Added:
1. State fields: Static _minimapWidth/_minimapHeight (preserved in memory across plans, not on disk), drag/resize state, node mapping for minimap interaction
2. Toggle/Open/Close: MinimapToggle_Click(object?, RoutedEventArgs), OpenMinimapPanel(), CloseMinimapPanel()
3. Rendering (RenderMinimap()):
• Branch areas: Each child subtree of the root gets a transparent colored rectangle (8 distinct colors cycling) behind its nodes
• Edges: Scaled-down elbow connectors
• Nodes: Small colored rectangles (red tint for expensive nodes)
• Viewport box: Semi-transparent blue rectangle showing the visible portion of the plan
4. Interaction:
• Click & drag on minimap to pan the plan viewer
• Single click on a node centers the plan viewer on that node
• Double click on a node zooms to ~1/3 viewport size and selects the node
• Resize grip (bottom-right corner) allows resizing between 200×200 and 500×500
5. Live updates: Viewport box updates on scroll, zoom, pan, and mouse wheel zoom. Minimap re-renders on statement change.
6. Per-plan: Each PlanViewerControl instance has its own minimap state, so multiple open plans each have their own minimap.

* 1. Minimap border: Increased from 1 to 2 px thickness
2. Resize grip: Moved from the canvas (where it got destroyed on every re-render) to a permanent AXAML element — a Border with 3 diagonal lines in the bottom-right corner indicating resizability, wired to the existing resize handlers in the constructor
3. Node borders: Changed from EdgeBrush (dark grey #6B7280) to a new MinimapNodeBorderBrush (#A0A4AB — light grey)
4. Node content: Each minimap node now shows its operator icon (scaled to fit, ~70% of the node size, max 16px)
5. Default size: Changed from 300×300 to 400×400

* 1. Minimap border now matches the tooltip/popup style: background #1A1D23, border #3A3D45, 5px thickness, same for header and canvas background
2. Selected node highlighting: When a node is selected in the plan viewer, the corresponding minimap node gets a blue selection border (SelectionBrush, 2px). The highlight persists through minimap re-renders and resets when a different node is selected.
3. Proportional edge thickness: Minimap edges now use the same logarithmic row-count formula as the plan viewer (Math.Log(rows) capped at 2–12), scaled down by the minimap scale factor, with a minimum of 0.5px.

* remove hardcoded colors

* Fixes
Fixes:
1 _selectedNode cleared Added _selectedNode = null in both Clear() and RenderStatement()
2 Dead field removed Removed _minimapDragStart field and its assignment
3 Handlers wired once Moved MinimapCanvas.PointerPressed/Moved/Released subscriptions to constructor, removed per-render -=/+= cycle
4 Unused constant removed Removed MinimapDefaultSize
5 Guard added RenderMinimap() now returns early with if (!MinimapPanel.IsVisible) return; before doing any work
6 Correct brush restored Deselect now uses _minimapNodeBorderBrushCache (matches creation brush) instead of FindBrushResource("BorderBrush")
7 Branch colors extracted Moved to static readonly Color[] MinimapBranchColors field — no allocation per render
8 Node border brush cached Computed once per render cycle in RenderMinimap() and stored in _minimapNodeBorderBrushCache, reused for all non-expensive nodes

* reverse bug fix (to do in another PR)

* Replace the Parent!.Parent! pattern with an x:Name-resolved field on the outer Grid.

* Hoisted the expensive-node background brush to static readonly MinimapExpensiveNodeBgBrush — no more per-node allocation in the render loop.

* align to main
Cleanup of cosmetic regression from #276: tabs were left on 3 lines around
ShowError that don't match the surrounding 4-space-indented file.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rious dirty state (#284)

- Show "Keyword Casing" instead of "KeywordCasing" in the Parameter column
- Row setters were firing PropertyChanged unconditionally; TwoWay bindings on
  ToggleSwitch / ComboBox write the bound value back during DataGrid row
  materialization (post-virtualization), tripping the dirty flag without any
  user action. Setters now compare and short-circuit when the value is unchanged.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r Parallelism subtypes (#285)

Columnstore: PhysicalOp surfaces as "Clustered Index Scan" / "Index Scan" /
"Columnstore Index Scan" with Storage="ColumnStore" on the Object element.
The mapper previously only inspected PhysicalOp, leaving the columnstore_*
icons unused. PlanIconMapper.GetIconName now takes an optional storageType
and routes to the columnstore variant when Storage="ColumnStore". Covers
both clustered (CCI) and nonclustered (NCCI) columnstore for scan, delete,
insert, update, and merge.

Parallelism (Repartition / Distribute / Gather Streams): all three share
PhysicalOp="Parallelism" and the same parallelism.png icon. Following the
overlay approach used by microsoft/vscode-mssql, render a small R/D/G glyph
in the bottom-right corner of the icon to distinguish them at a glance.
LogicalOp determines the letter; non-parallelism nodes get no overlay.

Closes #283.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FetchTopPlansAsync applied user filters (--query-id, --plan-id, --query-hash,
--query-plan-hash, --module) only in Phase 4, AFTER Phase 3 had already
selected TOP N plans by CPU across the entire database. If the user's target
query wasn't in the top N by CPU during the time window, it was cut from
#top_plans before the filter ever ran — even though Query Store / SSMS /
sp_QuickieStore could see it fine.

Move the filter into Phase 3's CTE so TOP N is selected from the filtered
universe. Conditionally JOIN sys.query_store_query when the filter touches
query_hash or object_id (otherwise the join is skipped to avoid penalty in
the unfiltered path). Phase 4 filter clause is now redundant and removed.

The hash-tree and module-tree methods (FetchByQueryHashTree, FetchByPlanHashTree)
already applied filters pre-TOP-N — they're unaffected.

Closes #286.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the temporary letter-overlay approach with three real icons
contributed by @rferraton (MIT-licensed for the repo): repartition_streams,
distribute_streams, and gather_streams (the latter is a horizontal mirror of
distribute, which matches SSMS's convention).

PlanIconMapper.GetIconName now also takes LogicalOp so the three Parallelism
subtypes route to their own icon. The overlay rendering and GetParallelismGlyph
helper in PlanViewerControl are removed.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* AppSettings (AppSettingsService.cs)
• Added AccuracyRatioDivergenceLimit property (default: 10), persisted as "accuracy_ratio_divergence_limit" in the JSON settings file.
PlanViewerControl (PlanViewerControl.axaml.cs)
• 6 new link color brushes for dark theme:
• Underestimated (more actual than estimated): Blue → Light Blue → Fluo Blue
• Overestimated (fewer actual than estimated): Light Orange → Fluo Orange → Fluo Red
• GetLinkColorBrush(PlanNode, double) method — maps accuracy ratio to a color band:
• [0, 1/(limit×100)) → Fluo Blue
• [1/(limit×100), 1/(limit×10)) → Light Blue
• [1/(limit×10), 1/limit) → Blue
• [1/limit, limit] → Default edge color (neutral)
• (limit, limit×10) → Light Orange
• [limit×10, limit×100) → Fluo Orange
• [limit×100, +∞) → Fluo Red
• CreateElbowConnector(PlanNode, PlanNode) now uses GetLinkColorBrush(PlanNode, double) instead of the static EdgeBrush, but only colors links differently for actual plans (estimated plans keep the default).

* minimap colored links

* Internal Code review and some fixes

* AppSettingsService.Load().AccuracyRatioDivergenceLimit is no more done per node but only once
* Update README.md: enhance Query Store section and add minimap feature description with screenshots

* Update README.md: add minimap feature for navigation and color-coded links for operator accuracy in the features list

Co-authored-by: Copilot <copilot@github.com>

* typo

---------

Co-authored-by: Copilot <copilot@github.com>
@erikdarlingdata erikdarlingdata mentioned this pull request Apr 27, 2026
2 tasks
Highlights since v1.8.0:
- Minimap for plan navigation (#276)
- Colored links by accuracy ratio divergence (#289)
- Distinct parallelism subtype icons (#285, #288)
- Query Store filter ordering fix (#287)
- xunit v2 -> v3 migration (#278)
- SqlClient 6 -> 7, ScriptDom 170 -> 180 (#279)
- System.CommandLine GA (#280)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@erikdarlingdata erikdarlingdata merged commit 66be6df into main Apr 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Query ID Not Appearing in Performance Studio Search Results [BUG] : some icons could be improved in plans

2 participants