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
6 changes: 6 additions & 0 deletions Dashboard/Services/PlanIconMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public static class PlanIconMapper
["Index Scan"] = "index_scan",
["Index Seek"] = "index_seek",
["Index Spool"] = "index_spool",
["Eager Index Spool"] = "index_spool",
["Lazy Index Spool"] = "index_spool",
["Index Update"] = "index_update",

// Columnstore
Expand Down Expand Up @@ -74,7 +76,11 @@ public static class PlanIconMapper

// Spool
["Table Spool"] = "table_spool",
["Eager Table Spool"] = "table_spool",
["Lazy Table Spool"] = "table_spool",
["Row Count Spool"] = "row_count_spool",
["Eager Row Count Spool"] = "row_count_spool",
["Lazy Row Count Spool"] = "row_count_spool",
["Window Spool"] = "table_spool",
["Eager Spool"] = "table_spool",
["Lazy Spool"] = "table_spool",
Expand Down
37 changes: 36 additions & 1 deletion Dashboard/Services/ShowPlanParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,19 @@ private static PlanNode ParseRelOp(XElement relOpEl)
StatsCollectionId = ParseLong(relOpEl.Attribute("StatsCollectionId")?.Value)
};

// Spool operators: prepend Eager/Lazy from LogicalOp to PhysicalOp
// XML has PhysicalOp="Index Spool" but LogicalOp="Eager Spool" — show "Eager Index Spool"
if (node.PhysicalOp.EndsWith("Spool", StringComparison.OrdinalIgnoreCase)
&& node.LogicalOp.StartsWith("Eager", StringComparison.OrdinalIgnoreCase))
{
node.PhysicalOp = "Eager " + node.PhysicalOp;
}
else if (node.PhysicalOp.EndsWith("Spool", StringComparison.OrdinalIgnoreCase)
&& node.LogicalOp.StartsWith("Lazy", StringComparison.OrdinalIgnoreCase))
{
node.PhysicalOp = "Lazy " + node.PhysicalOp;
}

// Map to icon
node.IconName = PlanIconMapper.GetIconName(node.PhysicalOp);

Expand Down Expand Up @@ -1429,10 +1442,32 @@ private static List<PlanWarning> ParseWarningsFromElement(XElement warningsEl)

if (warningsEl.Attribute("UnmatchedIndexes")?.Value is "true" or "1")
{
var unmatchedMsg = "Indexes could not be matched due to parameterization";
var unmatchedEl = warningsEl.Element(Ns + "UnmatchedIndexes");
if (unmatchedEl != null)
{
var unmatchedDetails = new List<string>();
foreach (var paramEl in unmatchedEl.Elements(Ns + "Parameterization"))
{
var db = paramEl.Attribute("Database")?.Value?.Replace("[", "").Replace("]", "");
var schema = paramEl.Attribute("Schema")?.Value?.Replace("[", "").Replace("]", "");
var table = paramEl.Attribute("Table")?.Value?.Replace("[", "").Replace("]", "");
var index = paramEl.Attribute("Index")?.Value?.Replace("[", "").Replace("]", "");
var parts = new List<string>();
if (!string.IsNullOrEmpty(db)) parts.Add(db);
if (!string.IsNullOrEmpty(schema)) parts.Add(schema);
if (!string.IsNullOrEmpty(table)) parts.Add(table);
if (!string.IsNullOrEmpty(index)) parts.Add(index);
if (parts.Count > 0)
unmatchedDetails.Add(string.Join(".", parts));
}
if (unmatchedDetails.Count > 0)
unmatchedMsg += ": " + string.Join(", ", unmatchedDetails);
}
result.Add(new PlanWarning
{
WarningType = "Unmatched Indexes",
Message = "Indexes could not be matched due to parameterization",
Message = unmatchedMsg,
Severity = PlanWarningSeverity.Warning
});
}
Expand Down
6 changes: 6 additions & 0 deletions Lite/Services/PlanIconMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public static class PlanIconMapper
["Index Scan"] = "index_scan",
["Index Seek"] = "index_seek",
["Index Spool"] = "index_spool",
["Eager Index Spool"] = "index_spool",
["Lazy Index Spool"] = "index_spool",
["Index Update"] = "index_update",

// Columnstore
Expand Down Expand Up @@ -74,7 +76,11 @@ public static class PlanIconMapper

// Spool
["Table Spool"] = "table_spool",
["Eager Table Spool"] = "table_spool",
["Lazy Table Spool"] = "table_spool",
["Row Count Spool"] = "row_count_spool",
["Eager Row Count Spool"] = "row_count_spool",
["Lazy Row Count Spool"] = "row_count_spool",
["Window Spool"] = "table_spool",
["Eager Spool"] = "table_spool",
["Lazy Spool"] = "table_spool",
Expand Down
37 changes: 36 additions & 1 deletion Lite/Services/ShowPlanParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,19 @@ private static PlanNode ParseRelOp(XElement relOpEl)
StatsCollectionId = ParseLong(relOpEl.Attribute("StatsCollectionId")?.Value)
};

// Spool operators: prepend Eager/Lazy from LogicalOp to PhysicalOp
// XML has PhysicalOp="Index Spool" but LogicalOp="Eager Spool" — show "Eager Index Spool"
if (node.PhysicalOp.EndsWith("Spool", StringComparison.OrdinalIgnoreCase)
&& node.LogicalOp.StartsWith("Eager", StringComparison.OrdinalIgnoreCase))
{
node.PhysicalOp = "Eager " + node.PhysicalOp;
}
else if (node.PhysicalOp.EndsWith("Spool", StringComparison.OrdinalIgnoreCase)
&& node.LogicalOp.StartsWith("Lazy", StringComparison.OrdinalIgnoreCase))
{
node.PhysicalOp = "Lazy " + node.PhysicalOp;
}

// Map to icon
node.IconName = PlanIconMapper.GetIconName(node.PhysicalOp);

Expand Down Expand Up @@ -1429,10 +1442,32 @@ private static List<PlanWarning> ParseWarningsFromElement(XElement warningsEl)

if (warningsEl.Attribute("UnmatchedIndexes")?.Value is "true" or "1")
{
var unmatchedMsg = "Indexes could not be matched due to parameterization";
var unmatchedEl = warningsEl.Element(Ns + "UnmatchedIndexes");
if (unmatchedEl != null)
{
var unmatchedDetails = new List<string>();
foreach (var paramEl in unmatchedEl.Elements(Ns + "Parameterization"))
{
var db = paramEl.Attribute("Database")?.Value?.Replace("[", "").Replace("]", "");
var schema = paramEl.Attribute("Schema")?.Value?.Replace("[", "").Replace("]", "");
var table = paramEl.Attribute("Table")?.Value?.Replace("[", "").Replace("]", "");
var index = paramEl.Attribute("Index")?.Value?.Replace("[", "").Replace("]", "");
var parts = new List<string>();
if (!string.IsNullOrEmpty(db)) parts.Add(db);
if (!string.IsNullOrEmpty(schema)) parts.Add(schema);
if (!string.IsNullOrEmpty(table)) parts.Add(table);
if (!string.IsNullOrEmpty(index)) parts.Add(index);
if (parts.Count > 0)
unmatchedDetails.Add(string.Join(".", parts));
}
if (unmatchedDetails.Count > 0)
unmatchedMsg += ": " + string.Join(", ", unmatchedDetails);
}
result.Add(new PlanWarning
{
WarningType = "Unmatched Indexes",
Message = "Indexes could not be matched due to parameterization",
Message = unmatchedMsg,
Severity = PlanWarningSeverity.Warning
});
}
Expand Down
Loading