Skip to content

Release v1.6.0#243

Merged
erikdarlingdata merged 8 commits intomainfrom
dev
Apr 20, 2026
Merged

Release v1.6.0#243
erikdarlingdata merged 8 commits intomainfrom
dev

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

v1.6.0 Release

Highlights

  • Wait stats benefit scoring (Stage 2 of [FEATURE] Add system to assign a "maximum benefit" for plan analysis rules #215) — wait stats now contribute to the max-benefit calculation in PlanAnalyzer findings
  • CPU:Elapsed ratio replaces the old parallelism-efficiency warnings for clearer parallel-plan diagnosis
  • SQL Formatter in the Query Editor (Feature/query editor sql formater #238) with persistent format options (parameters dialog, async formatting, parse-error popup)
  • Query Store group-by changer (Feature/query store group by changer #218) — group plans by QueryHash or Module, with auto-expand, golden headers, and reordered columns
  • Built-in analytics for PlanShare and stats dashboard
  • Joe's plan-review feedback — false-positive fixes, improved messaging, Rule 34 narrowing, Serial Plan / adaptive join math, MAXDOP 1 wording, Scalar UDF redundancy, parallel-skew + key-lookup NL join scoring

Other

🤖 Generated with Claude Code

ClaudioESSilva and others added 8 commits April 17, 2026 16:28
- Query Store grid: dropped "(Local)" from Last Executed (the Time display
  dropdown already controls timezone), shortened "Physical Reads" to
  "Phys Reads", and widened CPU / Duration / Memory / Phys Reads /
  Executions columns by 5-15px so the "(ms)" and "(MB)" unit suffixes
  no longer truncate in the default layout.
- Toolbar density: reduced button padding 10,0 -> 8,0, StackPanel Spacing
  6 -> 4, and pipe-separator margins 4,0 -> 2,0 (and 12,0 -> 6,0) across
  both QuerySessionControl and PlanViewerControl toolbars. Dropped
  redundant per-button margins now that Spacing handles them.
- Wait palette: Worker Thread #19ff25 -> #52E3B5 (teal, distinct from CPU
  green), Parallelism #fd01d3 -> #7B4FFF (vivid purple, out of the
  pink/magenta cluster) so neighboring wait categories read as different
  colors on the stacked wait profile bar.
- Added SystemAccentColor (+ Dark1/Dark2/Light1/Light2 variants) to
  DarkTheme. Fluent theme's TabItem selected pipe reads from
  SystemAccentColor, so Plan 1 / Plan 2 tabs now get the brand-blue
  underline consistently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Polish PlanViewer toolbars, Query Store headers, and tab accent
Clears CS8933 and CS8019 — System is already covered by the SDK's
implicit global usings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sing-serverconnection

Remove redundant System using in ServerConnection
* Query Editor SQL Formater with parameters options - init working

* improve parameter format option grid ux

* undo possible after format

* Dynamic brushes instead of hard coded color

* remove unused using lib

* Fix Reviewer remarks phase 1

* Fix Second-opinion addendum

* fix query editor content disapear if format have a syntax error. Now a popup message appears with the syntax error

* 1. Hardcoded colors in error dialog → Now uses (IBrush)this.FindResource("BackgroundBrush")!
2. Close without dirty-state prompt : fixed
3. Tooltip Ctrl+K,Ctrl+D : removed

* dynamic brush for formatoptions

* Build passes. All three callers of Load(out string?)/Save(SqlFormatSettings, out string?) now surface errors:
The format error dialog was launched fire-and-forget, causing status
text to update and the method to return before the user dismissed it.

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

@erikdarlingdata erikdarlingdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #243 — v1.6.0 release review

What it does
Merges devmain for v1.6.0. The 12 files in this diff cover: the new SQL Formatter (SqlFormattingService + SqlFormatSettingsService + FormatOptionsWindow + Format/Format Options buttons in QuerySessionControl), a fix for Query Editor losing syntax highlighting on tab switch (#234), PlanViewer / Query Store toolbar polish (padding/spacing/column width tweaks and header renames), two WaitCategory color swaps in DarkTheme.axaml, a Fluent SystemAccentColor* override, a removed unused using System; in ServerConnection.cs, the v1.6.0 csproj bump, and THIRD-PARTY-NOTICES.md for SqlFormatter attribution.

Base branch: main. Per the standard rule, PRs target dev — but this is the release merge, so it's intentional. Calling it out for the record.

PR description vs. diff mismatch. The description claims wait-stats benefit scoring (Stage 2 of #215), CPU:Elapsed ratio, Query Store group-by changer (#218), PlanShare analytics, Joe's plan-review feedback (Rule 34 narrowing, Serial Plan / adaptive join math, Scalar UDF, parallel-skew + key-lookup NL scoring), etc. None of those are in this diff — there are no PlanViewer.Core rule/scoring changes here. Presumably they landed via earlier dev→main merges. Worth reconciling the release notes before tagging, otherwise the changelog will mislead users scanning the commit range.

PlanAnalyzer sync check: Since no PlanAnalyzer rules are touched here, nothing to sync to PerformanceMonitor Dashboard / Lite for this PR. If any rule changes did land on dev since the last release, verify those were mirrored at the time.

What's good

  • SQL Formatter is correctly implemented as a Task.Run wrapper so parsing doesn't block the UI thread.
  • QueryEditor.Document.BeginUpdate() / EndUpdate() around Replace preserves undo and avoids intermediate re-rendering; caret offset is clamped to new length.
  • SetupSyntaxHighlighting re-install on re-attach is a clean fix for the #234 tab-switch issue.
  • PlaceholderText (not Watermark) is used correctly on ComboBoxes.
  • New WaitCategory colors (#52E3B5, #7B4FFF) are bright/saturated — not in the rejected #6B7280/dim-grey range.
  • SqlFormatSettingsService round-trips to LocalApplicationData with reasonable error surfaces.

What needs attention (see inline comments)

  1. FormatOptionRow : INotifyPropertyChanged — MVVM creep that the repo explicitly avoids.
  2. Save_Click silently swallows bad int input and generic exceptions; the user gets no feedback that their edit was dropped.
  3. OnClosing fire-and-forgets an async void TryClose() that can crash the app on exception. Format_Click has the same async void exposure.
  4. Revert_Click flips _isDirty=true unnecessarily, triggering a spurious "Unsaved Changes" prompt.
  5. SqlFormattingService.Format generates the script even when parse errors exist and callers discard it.
  6. SetupSyntaxHighlighting re-install — quick perf/leak sanity check on heavy multi-tab sessions.
  7. No validation on KeywordCasing string at save time (low risk given the ComboBox UI).

Test coverage. New logic in src/PlanViewer.App/Services/ (SqlFormattingService, SqlFormatSettingsService) has no corresponding tests. tests/PlanViewer.Core.Tests/ is Core-scoped so it isn't the natural home — but if there's no PlanViewer.App.Tests, the settings round-trip and format-happy-path at minimum are easy to cover and would catch regressions on the ScriptDom dependency bump.

Comments only — not approving or requesting changes. Maintainer's call.


Generated by Claude Code

}
}

public class FormatOptionRow : INotifyPropertyChanged
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FormatOptionRow : INotifyPropertyChanged is MVVM creep. Repo convention is code-behind only — no INotifyPropertyChanged, no bindings-to-VM. The DataGrid needs change notification only for the BoolValueCurrentValue sync; that can be handled imperatively from the code-behind (e.g., wire ToggleSwitch.IsCheckedChanged in cell templates, or rebuild the rows after edits). If you keep INPC here, it sets a precedent that will leak into the rest of the app.


Generated by Claude Code

Comment on lines +109 to +124
if (!int.TryParse(row.CurrentValue, out var intVal))
{
Debug.WriteLine($"FormatOptions: invalid int value '{row.CurrentValue}' for {row.Name}, using default");
continue;
}
value = intVal;
}
else
value = row.CurrentValue;

prop.SetValue(settings, value);
}
catch (Exception ex)
{
Debug.WriteLine($"FormatOptions: failed to set {row.Name}: {ex.Message}");
}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both error paths here silently drop the user's edit with only a Debug.WriteLine:

  • L109-113: invalid int → continue, the previously-saved value is silently kept.
  • L121-124: blanket catch (Exception) → same.

From the user's perspective, they typed something, clicked Save, the dialog closed, and the setting didn't change. There's no feedback. Either surface a validation error (reuse ShowErrorPopup with return; so nothing saves) or constrain the editor so a bad value can't be typed in the first place. Also consider int.TryParse(row.CurrentValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out …) since the JSON file is invariant.


Generated by Claude Code

Comment on lines +178 to +189
protected override void OnClosing(WindowClosingEventArgs e)
{
if (_isDirty)
{
e.Cancel = true;
TryClose();
return;
}
base.OnClosing(e);
}

private async void TryClose()
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnClosing fires-and-forgets an async void TryClose(). Any exception from ShowDiscardDialog (e.g., FindResource miss on a themed resource, dialog owner already closed) propagates to the SynchronizationContext as an unhandled exception and crashes the app. Wrap the body of TryClose in try/catch and log, or make it return Task and await it from a single-purpose async void shim. Same concern applies to Format_Click in QuerySessionControl.axaml.cs:2009 — the try/finally there doesn't catch, so SqlFormattingService.Format / ShowDialog exceptions bubble out of an async void.


Generated by Claude Code

Comment on lines +136 to +144
private void Revert_Click(object? sender, RoutedEventArgs e)
{
foreach (var row in _rows)
{
row.CurrentValue = row.DefaultValue;
if (row.IsBool)
row.BoolValue = row.DefaultBoolValue;
}
}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert_Click mutates CurrentValue / BoolValue, which fires the PropertyChanged handler added in LoadSettings (L91) and flips _isDirty = true, even when the user reverts to defaults and never had pending edits. Net effect: click Revert → Close → "Unsaved Changes" prompt appears unnecessarily. Consider suppressing the dirty flag during revert, or comparing against defaults before setting _isDirty.


Generated by Claude Code


if (fragment == null)
return (sql, errors);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If parser.Parse returns a non-null fragment but errors is non-empty (it's designed to recover), you still run GenerateScript against the partially-valid AST and return it as formattedText. Callers in QuerySessionControl.Format_Click (L2026) treat any non-null errors as "format failed, show dialog and bail" — so the generated output is discarded. That's the safer path, but it means this method is doing work that gets thrown away. Either bail here too when errors are present, or let callers decide without double-generating.


Generated by Claude Code

Comment on lines 72 to +86
AttachedToVisualTree += (_, _) =>
{
if (_textMateInstallation == null)
SetupSyntaxHighlighting();

QueryEditor.Focus();
QueryEditor.TextArea.Focus();
};

DetachedFromVisualTree += (_, _) => _textMateInstallation?.Dispose();
// Dispose TextMate when detached (e.g. tab switch) to release renderers/transformers
DetachedFromVisualTree += (_, _) =>
{
_textMateInstallation?.Dispose();
_textMateInstallation = null;
};
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix for #234 looks right. Two notes:

  1. SetupSyntaxHighlighting() is called on every re-attach; confirm it doesn't leak the RegistryOptions / grammar objects (the old _textMateInstallation?.Dispose() only releases the installation, not whatever InstallTextMate allocated on the editor). If it's cheap, fine.
  2. Tab-switch churn now runs a full TextMate reinstall every time — worth a quick perf sanity check with a busy session that has many tabs.

Generated by Claude Code

IndentSetClause = IndentSetClause,
IndentViewBody = IndentViewBody,
IndentationSize = IndentationSize,
KeywordCasing = Enum.TryParse<Microsoft.SqlServer.TransactSql.ScriptDom.KeywordCasing>(KeywordCasing, true, out var kc)
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KeywordCasing round-trips via ToString() / Enum.TryParse. Persisting the string form is fine, but there's no validation on Save — a user can type any text in the options grid (if the combo cell is edited as text), it'll get saved to JSON, and on next Load the Enum.TryParse silently falls back to Uppercase. Not a real risk with the current ComboBox-backed editor, but worth validating on Save so bad state never hits disk.


Generated by Claude Code

@erikdarlingdata erikdarlingdata merged commit 2958568 into main Apr 20, 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.

3 participants