diff --git a/src/PlanViewer.Core/Output/AnalysisResult.cs b/src/PlanViewer.Core/Output/AnalysisResult.cs
index c9c1a26..04f6f4e 100644
--- a/src/PlanViewer.Core/Output/AnalysisResult.cs
+++ b/src/PlanViewer.Core/Output/AnalysisResult.cs
@@ -167,6 +167,20 @@ public class MemoryGrantResult
[JsonPropertyName("estimated_available_memory_grant_kb")]
public long EstimatedAvailableMemoryGrantKB { get; set; }
+
+ ///
+ /// Optimizer's pre-execution "desired" grant (parallel-adjusted).
+ /// Non-zero on estimated plans; pairs with DesiredKB serial-required as fallback
+ /// when no runtime-granted memory exists (#215 E6).
+ ///
+ [JsonPropertyName("desired_kb")]
+ public long DesiredKB { get; set; }
+
+ ///
+ /// Optimizer's pre-execution serial-required grant (memory minimum before DOP scaling).
+ ///
+ [JsonPropertyName("serial_required_kb")]
+ public long SerialRequiredKB { get; set; }
}
public class QueryTimeResult
diff --git a/src/PlanViewer.Core/Output/HtmlExporter.cs b/src/PlanViewer.Core/Output/HtmlExporter.cs
index 5d87f26..cf8ac8a 100644
--- a/src/PlanViewer.Core/Output/HtmlExporter.cs
+++ b/src/PlanViewer.Core/Output/HtmlExporter.cs
@@ -335,6 +335,13 @@ private static void WriteRuntimeCard(StringBuilder sb, StatementResult stmt)
var spillTag = hasSpill ? " ⚠ spill" : "";
sb.AppendLine($"
Used{FormatKB(stmt.MemoryGrant.MaxUsedKB)} ({pctUsed:N0}%){spillTag}
");
}
+ else if (isEstimated && stmt.MemoryGrant != null && stmt.MemoryGrant.DesiredKB > 0)
+ {
+ // #215 E6: estimated plans — show the optimizer's pre-execution desired grant
+ WriteRow(sb, "Memory (estimated)", FormatKB(stmt.MemoryGrant.DesiredKB) + " desired");
+ if (stmt.MemoryGrant.SerialRequiredKB > 0 && stmt.MemoryGrant.SerialRequiredKB != stmt.MemoryGrant.DesiredKB)
+ WriteRow(sb, "Serial required", FormatKB(stmt.MemoryGrant.SerialRequiredKB));
+ }
if (stmt.OptimizationLevel != null)
WriteRow(sb, "Optimization", Encode(stmt.OptimizationLevel));
if (stmt.CardinalityEstimationModel > 0)
diff --git a/src/PlanViewer.Core/Output/ResultMapper.cs b/src/PlanViewer.Core/Output/ResultMapper.cs
index f73dd06..35d1f51 100644
--- a/src/PlanViewer.Core/Output/ResultMapper.cs
+++ b/src/PlanViewer.Core/Output/ResultMapper.cs
@@ -105,7 +105,9 @@ private static StatementResult MapStatement(PlanStatement stmt)
MaxUsedKB = stmt.MemoryGrant.MaxUsedMemoryKB,
GrantWaitMs = stmt.MemoryGrant.GrantWaitTimeMs,
FeedbackAdjusted = stmt.MemoryGrant.IsMemoryGrantFeedbackAdjusted,
- EstimatedAvailableMemoryGrantKB = stmt.HardwareProperties?.EstimatedAvailableMemoryGrant ?? 0
+ EstimatedAvailableMemoryGrantKB = stmt.HardwareProperties?.EstimatedAvailableMemoryGrant ?? 0,
+ DesiredKB = stmt.MemoryGrant.DesiredMemoryKB,
+ SerialRequiredKB = stmt.MemoryGrant.SerialRequiredMemoryKB
};
}
diff --git a/src/PlanViewer.Web/Pages/Index.razor b/src/PlanViewer.Web/Pages/Index.razor
index 652583c..8fbfdac 100644
--- a/src/PlanViewer.Web/Pages/Index.razor
+++ b/src/PlanViewer.Web/Pages/Index.razor
@@ -215,6 +215,20 @@ else
}
+ else if (isEstimatedRuntime && ActiveStmt!.MemoryGrant != null && ActiveStmt!.MemoryGrant.DesiredKB > 0)
+ {
+
+ Memory (estimated)
+ @FormatKB(ActiveStmt!.MemoryGrant.DesiredKB) desired
+
+ @if (ActiveStmt!.MemoryGrant.SerialRequiredKB > 0 && ActiveStmt!.MemoryGrant.SerialRequiredKB != ActiveStmt!.MemoryGrant.DesiredKB)
+ {
+
+ Serial required
+ @FormatKB(ActiveStmt!.MemoryGrant.SerialRequiredKB)
+
+ }
+ }
@if (ActiveStmt!.OptimizationLevel != null)
{