Skip to content

Add Query Store server-side filter panel, chips, and pre-filters#392

Merged
erikdarlingdata merged 1 commit into
devfrom
feature/qs-filter-panel
Jul 17, 2026
Merged

Add Query Store server-side filter panel, chips, and pre-filters#392
erikdarlingdata merged 1 commit into
devfrom
feature/qs-filter-panel

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Summary

  • Builds the full server-side Query Store filtering architecture on top of Add Query Store query-text search filter #390's query-text search: users can now stack sp_QuickieStore-style pre-filters that apply before TOP N in all three SQL builders, so target queries are found even when they aren't in the top N by the chosen metric.
  • New criteria (three-surface parity — Desktop, MCP, CLI): minimum executions, minimum average duration/CPU per execution (ms, over the selected window — deliberately grid-consistent rather than mirroring the proc's per-interval semantics), query-text exclude, an escape-brackets toggle for literal [ ] _ matching (EF-style bracketed text), and include/ignore lists for query IDs and plan IDs.
  • Desktop gains the filter architecture: a collapsible Server Filters panel with transactional Apply, an always-visible chip strip showing every criterion that shaped the fetch (chips are removable and re-fetch), and promotion — column-filter popups offer "Search server" on columns with server equivalents.

Closes #391

Changes

  • Core model (QueryStorePlan.cs): nine new QueryStoreFilter properties; ParseIdList (strict: ordered first-seen dedupe, throws naming any bad token); BuildQueryTextSearchPattern gains an escapeBrackets overload (wrap-then-escape, % never escaped, pairs with ESCAPE '\').
  • SQL builders (QueryStoreService.cs, QueryStoreService.Grouped.cs): all criteria added to the pre-TOP-N filter mechanism in the flat and both grouped builders, fully parameterized (per-element IN/NOT IN params); thresholds use null-safe > 0 guards and a float-literal multiplication form that avoids a numeric-precision overflow edge; rn = 1 is relaxed when plans are explicitly included so a non-best plan isn't dropped; with everything unset the generated SQL is byte-identical to before.
  • Filter-state model (QueryStoreServerFilterState.cs, Core — unit-testable): typed normalizing setters, ordered chip projection, 1:1 BuildFilter(); EscapeBrackets is a pure modifier (no chip, ignored by IsEmpty, always copied).
  • Desktop (QueryStoreGridControl.*, ColumnFilterPopup.*, AppSettingsService.cs): panel + chips rows; Search-by commits now flow into the shared filter state as chips; Fetch_Click commits before fetching; Clear clears all server filters and re-fetches; promotion maps Executions/AvgCpu/AvgDuration (measure-preserving; totals deliberately excluded), ids/hashes (Equals), text/module (Contains) with operator validation at click; panel expanded state persisted.
  • MCP (McpQueryStoreTools.cs): nine additive get_query_store_top params. CLI (QueryStoreCommand.cs): nine additive options. Malformed id lists error cleanly on both surfaces before any connection attempt.
  • Tests: 59 new — escape-mode pattern matrix (including the documented backslash parity limitation), full ParseIdList contract, and the filter-state model (chips, normalization, mapping, coexistence).

Test Plan

  • dotnet build PlanViewer.sln -c Debug — no new warnings
  • dotnet test — 182/182 (123 existing + 59 new)
  • Live end-to-end on SQL Server 2022 / StackOverflow2013: thresholds filter and back-fill pre-TOP-N; include/ignore id lists return exact sets; explicitly included plans survive the rn relaxation; --escape-brackets flips [Users] between literal and character-class matching; text include+exclude compose correctly (empty results ground-truthed against raw Query Store DMVs); stacked filters AND together
  • CLI --help shows all nine options; malformed --include-query-ids "1,x,3" exits 1 with a clear message before connecting
  • Desktop smoke: panel apply/clear, chip render/remove/re-fetch, Search-by-as-chips, promotion popup (code-verified and compiled; UI not driven — reviewer note: popup widened to 280px for the third button)

Generated with Claude Code

Core:
- QueryStoreFilter: MinExecutions, MinDurationMs, MinCpuMs (window-wide
  per-plan averages), QueryTextSearchNot, EscapeBrackets, and four
  include/ignore query-id/plan-id lists; strict ParseIdList (ordered
  dedupe, throws on garbage); BuildQueryTextSearchPattern escapeBrackets
  overload (escapes [ ] _ for ESCAPE '\', % stays live)
- All three SQL builders (flat, grouped-by-hash, grouped-by-module) gain
  the new clauses pre-TOP-N, fully parameterized; rn=1 relaxed when plans
  are explicitly included; unset path renders byte-identical SQL
- QueryStoreServerFilterState: UI-free, unit-testable filter model
  (typed setters, ordered chips, 1:1 BuildFilter projection)

Desktop:
- Collapsible Server Filters panel (thresholds, text include/exclude,
  escape toggle, id lists) with transactional Apply
- Chip strip showing every criterion that shaped the fetch; chips are
  removable and re-fetch
- Column-filter popups gain 'Search server' promotion on mapped columns
  (Executions/AvgCpu/AvgDuration/ids/hashes/text) with operator
  validation at click; totals deliberately not promotable
- Search-by commits become chips; Clear clears all server filters;
  panel expanded state persisted

MCP/CLI:
- Nine additive get_query_store_top params and query-store options

Tests: 59 new (escape-mode matrix, ParseIdList contract, filter-state
model) - 182 total passing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@erikdarlingdata
erikdarlingdata merged commit f073608 into dev Jul 17, 2026
2 checks passed
@erikdarlingdata
erikdarlingdata deleted the feature/qs-filter-panel branch July 17, 2026 00:38
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.

1 participant