Fix four analyzer rule gates (#577, #576, #579, #578) - #582
Conversation
#577: rule 5 reported a row-estimate mismatch on operators that never executed. Skip ActualExecutions = 0, as rules 11, 12 and 29 already do. #576: rule 6 dropped its scalar-UDF warning whenever the statement's NonParallelPlanReason was one rule 3 explains, even when rule 3 was disabled or gated out. Suppress it only when rule 3's Serial Plan finding is actually on the statement. #579: rules matched hints and keywords in the raw statement text, so string literals and comments counted as code. Add MaskCommentsAndLiterals and use it for rule 27 (OPTIMIZE FOR UNKNOWN) and for the same pattern in rule 3 (MAXDOP 1), rule 20 (RECOMPILE), rule 28 (NOT IN), rule 37 (cursor declaration) and the rule 26 row-goal cause. #578: rule 30 grouped missing-index suggestions by schema and table, so same-named tables in two databases looked like duplicates. Add the database to the key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G625JBNh45iTR1hpT4CxNR
|
Reviewed the diff against One gap: the PR says it audited "the raw statement text" checks and fixed six of them, but Rule 38 ( var hasMaxdop2Hint = Regex.IsMatch(MaskCommentsAndLiterals(stmt.StatementText), @"MAXDOP\s+2\b", RegexOptions.IgnoreCase);Everything else in the diff (rule 5's |
Review of #582 found rule 38 still matched MAXDOP 2 against the raw text, so a MAXDOP 2 mentioned in a comment suppressed the Standard Edition DOP warning. The app's parameters panel had the same raw OPTIMIZE FOR UNKNOWN check for its annotation. Both now use MaskCommentsAndLiterals, which is internal so the app can call it. Add direct tests for the helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G625JBNh45iTR1hpT4CxNR
|
Fixed in dcec793. Rule 38 now masks comments and string literals before it matches MAXDOP 2, and a new test covers it. I also searched the whole solution for the same pattern and found one more copy: the app's parameters panel matched OPTIMIZE FOR UNKNOWN in the raw text to choose its annotation. The same commit fixes that. |
|
Reviewed the diff (analyzer rule gates for #577, #576, #579, #578 plus the new
No correctness, injection, or convention issues found. One non-blocking nit: in |
The rule now runs only when ActualExecutions > 0, so the fallback to 1 can never apply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G625JBNh45iTR1hpT4CxNR
|
Reviewed the diff (fetch of Summary: looks correct. Walked through each of the four fixes:
Conventions checked: no Test coverage: the new I wasn't able to run |
What does this PR do?
Fixes #577, #576, #579 and #578. Each is a gate that decides whether an analyzer rule fires.
ActualExecutions = 0). An operator that never ran returned zero rows because it never ran, so the zero says nothing about the estimate. Rule 5 now skips such operators, as rules 11, 12 and 29 already do. I checked the other node rules that read actual-run values (rules 8, 14, 16, 26, 28, 32 and 35). Each one needs a non-zero actual value before it fires, so none of them has this problem. After the review, a later commit removed the fallback? node.ActualExecutions : 1in rule 5. The new gate means that the fallback can never run.NonParallelPlanReasonwas one of the three reasons that rule 3 explains. It assumed that rule 3 had fired. But rule 3 can be disabled, and it skips statements that cost under 1, TRIVIAL plans and 0 ms runs. In those cases the UDF warning disappeared and no warning replaced it. Rule 6 now drops its warning only when rule 3's "Serial Plan" finding is on the statement. Statement rules run before node rules, so the finding is already there when rule 6 runs.MaskCommentsAndLiteralshelper replaces string-literal contents and whole comments with spaces before the match. It handles'...'with doubled quotes,--comments and nested/* */comments. It steps over[...]and"..."identifiers unchanged, so a quote inside[it's]does not start a string. The scan follows the one inParameterSubstitution. Seven other checks had the same problem, and they now use the helper too:MAXDOP 1in a comment made rule 3 say "MAXDOP is set to 1 using a query hint"RECOMPILEin a comment removed the local-variables warningNOT INin a comment counted toward the NOT IN patternLOCALin a comment counted as the qualifierMAXDOP 2in a comment removed the Standard Edition DOP warning (found by the review of this PR)OPTIMIZE FOR UNKNOWNin a comment chose the wrong annotationdbo.Tin database A anddbo.Tin database B looked like duplicates. The rule then recommended that you consolidate them. The group key now includes the database. The message text did not change.Which component(s) does this affect?
The helper is
internalinPlanAnalyzer.Helpers.cs.PlanViewer.Web.csprojalready links that file, so the web build needs no new include. The app can call it because Core already hasInternalsVisibleToforPlanViewer.App.How was this tested?
AnalyzerRuleGateTests: 31 cases, built in code like the issues' repros. The first 26 ran against the unfixed analyzer, and 15 failed. They are every case that reproduces a bug. The later 5 cover rule 38 and the helper itself. The other 11 are controls for behavior that must not change. For example, a real hint still warns, andOPTION (RECOMPILE)still silences rule 20.PlanAnalyzerTestspass (159 of 159).Checklist
dotnet test)dotnet test)🤖 Generated with Claude Code
https://claude.ai/code/session_01G625JBNh45iTR1hpT4CxNR