@@ -90,8 +90,6 @@ public partial class PlanViewerControl : UserControl
9090 private static readonly SolidColorBrush OrangeBrush = new ( Colors . Orange ) ;
9191
9292
93- // Current property section for collapsible groups
94- private StackPanel ? _currentPropertySection ;
9593 // Track all property section grids for synchronized column resize
9694 private readonly List < ColumnDefinition > _sectionLabelColumns = new ( ) ;
9795 private double _propertyLabelWidth = 140 ;
@@ -118,7 +116,8 @@ public partial class PlanViewerControl : UserControl
118116 public PlanViewerControl ( )
119117 {
120118 InitializeComponent ( ) ;
121- PlanScrollViewer . PointerWheelChanged += PlanScrollViewer_PointerWheelChanged ;
119+ // Use Tunnel routing so Ctrl+wheel zoom fires before ScrollViewer consumes the event
120+ PlanScrollViewer . AddHandler ( PointerWheelChangedEvent , PlanScrollViewer_PointerWheelChanged , Avalonia . Interactivity . RoutingStrategies . Tunnel ) ;
122121 // Use Tunnel routing so pan handlers fire before ScrollViewer consumes the events
123122 PlanScrollViewer . AddHandler ( PointerPressedEvent , PlanScrollViewer_PointerPressed , Avalonia . Interactivity . RoutingStrategies . Tunnel ) ;
124123 PlanScrollViewer . AddHandler ( PointerMovedEvent , PlanScrollViewer_PointerMoved , Avalonia . Interactivity . RoutingStrategies . Tunnel ) ;
@@ -820,7 +819,6 @@ private async System.Threading.Tasks.Task SetClipboardTextAsync(string text)
820819 private void ShowPropertiesPanel ( PlanNode node )
821820 {
822821 PropertiesContent . Children . Clear ( ) ;
823- _currentPropertySection = null ;
824822 _sectionLabelColumns . Clear ( ) ;
825823 _currentSectionGrid = null ;
826824 _currentSectionRowIndex = 0 ;
@@ -1771,7 +1769,6 @@ private void AddPropertySection(string title)
17711769 HorizontalContentAlignment = HorizontalAlignment . Stretch
17721770 } ;
17731771 PropertiesContent . Children . Add ( expander ) ;
1774- _currentPropertySection = null ; // No longer used — rows go into _currentSectionGrid
17751772 }
17761773
17771774 private void AddPropertyRow ( string label , string value , bool isCode = false , bool indent = false )
@@ -2187,7 +2184,7 @@ private void ShowParameters(PlanStatement statement)
21872184
21882185 if ( parameters . Count == 0 )
21892186 {
2190- var localVars = FindUnresolvedVariables ( statement . StatementText , parameters ) ;
2187+ var localVars = FindUnresolvedVariables ( statement . StatementText , parameters , statement . RootNode ) ;
21912188 if ( localVars . Count > 0 )
21922189 {
21932190 ParametersHeader . Text = "Parameters" ;
@@ -2303,7 +2300,7 @@ private void ShowParameters(PlanStatement statement)
23032300 }
23042301 }
23052302
2306- var unresolved = FindUnresolvedVariables ( statement . StatementText , parameters ) ;
2303+ var unresolved = FindUnresolvedVariables ( statement . StatementText , parameters , statement . RootNode ) ;
23072304 if ( unresolved . Count > 0 )
23082305 {
23092306 AddParameterAnnotation (
@@ -2350,7 +2347,8 @@ private void AddParameterAnnotation(string text, string color)
23502347 } ) ;
23512348 }
23522349
2353- private static List < string > FindUnresolvedVariables ( string queryText , List < PlanParameter > parameters )
2350+ private static List < string > FindUnresolvedVariables ( string queryText , List < PlanParameter > parameters ,
2351+ PlanNode ? rootNode = null )
23542352 {
23552353 var unresolved = new List < string > ( ) ;
23562354 if ( string . IsNullOrEmpty ( queryText ) )
@@ -2359,6 +2357,11 @@ private static List<string> FindUnresolvedVariables(string queryText, List<PlanP
23592357 var extractedNames = new HashSet < string > (
23602358 parameters . Select ( p => p . Name ) , StringComparer . OrdinalIgnoreCase ) ;
23612359
2360+ // Collect table variable names from the plan tree so we don't misreport them as local variables
2361+ var tableVarNames = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
2362+ if ( rootNode != null )
2363+ CollectTableVariableNames ( rootNode , tableVarNames ) ;
2364+
23622365 var matches = Regex . Matches ( queryText , @"@\w+" , RegexOptions . IgnoreCase ) ;
23632366 var seenVars = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
23642367
@@ -2369,6 +2372,8 @@ private static List<string> FindUnresolvedVariables(string queryText, List<PlanP
23692372 continue ;
23702373 if ( varName . StartsWith ( "@@" , StringComparison . OrdinalIgnoreCase ) )
23712374 continue ;
2375+ if ( tableVarNames . Contains ( varName ) )
2376+ continue ;
23722377
23732378 seenVars . Add ( varName ) ;
23742379 unresolved . Add ( varName ) ;
@@ -2377,6 +2382,19 @@ private static List<string> FindUnresolvedVariables(string queryText, List<PlanP
23772382 return unresolved ;
23782383 }
23792384
2385+ private static void CollectTableVariableNames ( PlanNode node , HashSet < string > names )
2386+ {
2387+ if ( ! string . IsNullOrEmpty ( node . ObjectName ) && node . ObjectName . StartsWith ( "@" ) )
2388+ {
2389+ // ObjectName is like "@t.c" — extract the table variable name "@t"
2390+ var dotIdx = node . ObjectName . IndexOf ( '.' ) ;
2391+ var tvName = dotIdx > 0 ? node . ObjectName [ ..dotIdx ] : node . ObjectName ;
2392+ names . Add ( tvName ) ;
2393+ }
2394+ foreach ( var child in node . Children )
2395+ CollectTableVariableNames ( child , names ) ;
2396+ }
2397+
23802398 private static void CollectWarnings ( PlanNode node , List < PlanWarning > warnings )
23812399 {
23822400 warnings . AddRange ( node . Warnings ) ;
@@ -2961,9 +2979,17 @@ private async void SavePlan_Click(object? sender, RoutedEventArgs e)
29612979
29622980 if ( file != null )
29632981 {
2964- await using var stream = await file . OpenWriteAsync ( ) ;
2965- await using var writer = new StreamWriter ( stream ) ;
2966- await writer . WriteAsync ( _currentPlan . RawXml ) ;
2982+ try
2983+ {
2984+ await using var stream = await file . OpenWriteAsync ( ) ;
2985+ await using var writer = new StreamWriter ( stream ) ;
2986+ await writer . WriteAsync ( _currentPlan . RawXml ) ;
2987+ }
2988+ catch ( Exception ex )
2989+ {
2990+ System . Diagnostics . Debug . WriteLine ( $ "SavePlan failed: { ex . Message } ") ;
2991+ CostText . Text = $ "Save failed: { ( ex . Message . Length > 60 ? ex . Message [ ..60 ] + "..." : ex . Message ) } ";
2992+ }
29672993 }
29682994 }
29692995
0 commit comments