Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions src/PlanViewer.Core/Services/PlanAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -482,31 +482,39 @@ private static void AnalyzeStatement(PlanStatement stmt, AnalyzerConfig cfg, Ser
if (!cfg.IsRuleDisabled(38) && stmt.DegreeOfParallelism == 2 && stmt.RootNode != null
&& HasBatchModeNode(stmt.RootNode))
{
var editionKnown = !string.IsNullOrEmpty(serverMetadata?.Edition);
if (editionKnown
&& serverMetadata!.Edition!.Contains("Standard", StringComparison.OrdinalIgnoreCase))
// Suppress when the user explicitly set MAXDOP 2 as a query hint — the DOP
// cap is intentional, not the Standard Edition batch-mode limitation.
var hasMaxdop2Hint = !string.IsNullOrEmpty(stmt.StatementText)
&& Regex.IsMatch(stmt.StatementText, @"MAXDOP\s+2\b", RegexOptions.IgnoreCase);

if (!hasMaxdop2Hint)
{
// Server context confirms Standard Edition — check MAXDOP
if (serverMetadata.MaxDop > 2)
var editionKnown = !string.IsNullOrEmpty(serverMetadata?.Edition);
if (editionKnown
&& serverMetadata!.Edition!.Contains("Standard", StringComparison.OrdinalIgnoreCase))
{
// Server context confirms Standard Edition — check MAXDOP
if (serverMetadata.MaxDop > 2)
{
stmt.PlanWarnings.Add(new PlanWarning
{
WarningType = "Standard Edition DOP Limitation",
Message = $"DOP is limited to 2 because SQL Server Standard Edition caps parallelism at 2 when batch mode operators are present, even though MAXDOP is set to {serverMetadata.MaxDop}. Developer or Enterprise Edition would allow higher DOP in the same conditions.",
Severity = PlanWarningSeverity.Warning
});
}
}
else if (!editionKnown)
{
// No server context, or edition unknown (e.g. collection failure) — suspect the limitation
stmt.PlanWarnings.Add(new PlanWarning
{
WarningType = "Standard Edition DOP Limitation",
Message = $"DOP is limited to 2 because SQL Server Standard Edition caps parallelism at 2 when batch mode operators are present, even though MAXDOP is set to {serverMetadata.MaxDop}. Developer or Enterprise Edition would allow higher DOP in the same conditions.",
Severity = PlanWarningSeverity.Warning
Message = "DOP is limited to 2 and the plan uses batch mode operators. This may be caused by the SQL Server Standard Edition limitation, which caps parallelism at 2 when batch mode is in use. If this server runs Standard Edition, Developer or Enterprise Edition would allow higher DOP.",
Severity = PlanWarningSeverity.Info
});
}
}
else if (!editionKnown)
{
// No server context, or edition unknown (e.g. collection failure) — suspect the limitation
stmt.PlanWarnings.Add(new PlanWarning
{
WarningType = "Standard Edition DOP Limitation",
Message = "DOP is limited to 2 and the plan uses batch mode operators. This may be caused by the SQL Server Standard Edition limitation, which caps parallelism at 2 when batch mode is in use. If this server runs Standard Edition, Developer or Enterprise Edition would allow higher DOP.",
Severity = PlanWarningSeverity.Info
});
}
}

// Rules 25 (Ineffective Parallelism) and 31 (Parallel Wait Bottleneck) were removed.
Expand Down
33 changes: 33 additions & 0 deletions tests/PlanViewer.Core.Tests/PlanAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -989,5 +989,38 @@ public void Rule38_ServerMetadataWithNullEdition_Dop2_BatchMode_EmitsInfo()
Assert.Equal(PlanWarningSeverity.Info, warnings[0].Severity);
}

[Fact]
public void Rule38_StandardEdition_Dop2_BatchMode_MaxDop2QueryHint_NoWarning()
{
// User explicitly set OPTION (MAXDOP 2) — DOP cap is intentional, not the SE limit
var stmt = BuildBatchModeDop2Statement();
stmt.StatementText = "SELECT * FROM dbo.Fact OPTION (MAXDOP 2)";
var plan = BuildSyntheticPlan(stmt);
var metadata = new ServerMetadata
{
Edition = "Standard Edition (64-bit)",
MaxDop = 8
};
PlanAnalyzer.Analyze(plan, serverMetadata: metadata);

var warnings = stmt.PlanWarnings
.Where(w => w.WarningType == "Standard Edition DOP Limitation").ToList();
Assert.Empty(warnings);
}

[Fact]
public void Rule38_NoServerMetadata_Dop2_BatchMode_MaxDop2QueryHint_NoWarning()
{
// Same suppression applies when we don't know the edition either
var stmt = BuildBatchModeDop2Statement();
stmt.StatementText = "SELECT * FROM dbo.Fact OPTION (MAXDOP 2)";
var plan = BuildSyntheticPlan(stmt);
PlanAnalyzer.Analyze(plan);

var warnings = stmt.PlanWarnings
.Where(w => w.WarningType == "Standard Edition DOP Limitation").ToList();
Assert.Empty(warnings);
}

#endregion
}
Loading