@@ -361,8 +361,24 @@ def feed_metrics(self, query: str, latency_ms: float) -> None:
361361
362362 def explain_action (self , query : str , available_actions : List [str ]) -> Dict [str , Any ]:
363363 """
364- Explain what action would be taken without executing
365- Returns Q-values and predicted action for EXPLAIN command
364+ Explain what action would be taken without executing.
365+
366+ Returns Q-values and predicted action for EXPLAIN command.
367+ This method provides a read-only analysis of the optimizer's decision-making
368+ process without actually executing any action or updating the Q-table.
369+
370+ Args:
371+ query: SQL query string
372+ available_actions: List of possible actions
373+
374+ Returns:
375+ Dict containing:
376+ - state: state key string
377+ - q_values: Q-values for all actions
378+ - best_action: action with highest Q-value
379+ - epsilon: current exploration rate
380+ - would_explore: whether exploration is possible
381+ - explanation: human-readable explanation of optimizer behavior
366382 """
367383 state = f"query_type_{ len (query ) // 10 } "
368384
@@ -374,13 +390,16 @@ def explain_action(self, query: str, available_actions: List[str]) -> Dict[str,
374390
375391 best_action = max (available_actions , key = lambda a : q_values .get (a , 0.0 ))
376392
393+ # Defensive truncation for display (limit to 20 chars)
394+ best_action_display = best_action [:20 ] if len (best_action ) > 20 else best_action
395+
377396 return {
378397 'state' : state ,
379398 'q_values' : q_values ,
380- 'best_action' : best_action ,
399+ 'best_action' : best_action_display ,
381400 'epsilon' : self .epsilon ,
382401 'would_explore' : self .epsilon > 0 ,
383- 'exploration_note ' : f'Random action possible ( ε={ self .epsilon } )' if self .epsilon > 0 else 'Would use best action '
402+ 'explanation ' : f'With ε={ self .epsilon :.4f } , agent would explore { self .epsilon * 100 :.1f } % of the time '
384403 }
385404
386405
@@ -477,65 +496,79 @@ def explain_query_plan(query: str, cache: Optional[SemanticCache] = None,
477496
478497
479498def format_explain_output (explain_result : Dict [str , Any ]) -> str :
480- """Format EXPLAIN result as a readable table"""
499+ """Format EXPLAIN result as a readable table with defensive field width limits"""
500+
501+ def truncate (value : Any , max_len : int ) -> str :
502+ """Truncate value to max length for box alignment"""
503+ s = str (value )
504+ if len (s ) > max_len :
505+ return s [:max_len - 3 ] + "..."
506+ return s
507+
481508 lines = []
482509 lines .append ("=" * 70 )
483510 lines .append ("QUERY EXECUTION PLAN" )
484511 lines .append ("=" * 70 )
485512
486513 # Smart query truncation
487514 query = explain_result ['query' ]
488- if len (query ) > 60 :
489- display_query = query [:60 ] + "..."
490- else :
491- display_query = query
515+ display_query = truncate (query , 60 )
492516
493517 lines .append (f"Query: { display_query } " )
494518 lines .append ("" )
495519
496520 # Parsing section
497521 lines .append ("┌─ PARSING ─────────────────────────────────────────────────────────┐" )
498522 p = explain_result ['parsing' ]
499- lines .append (f"│ Type: { p ['query_type' ]:<15} Complexity: { p ['complexity_estimate' ]} /10 │" )
523+ query_type = truncate (p ['query_type' ], 15 )
524+ lines .append (f"│ Type: { query_type :<15} Complexity: { p ['complexity_estimate' ]} /10 │" )
500525 lines .append (f"│ WHERE: { str (p ['has_where_clause' ]):<8} JOIN: { str (p ['has_join' ]):<8} AGG: { str (p ['has_aggregation' ]):<8} │" )
501526 lines .append ("└───────────────────────────────────────────────────────────────────┘" )
502527 lines .append ("" )
503528
504529 # Cache section
505530 lines .append ("┌─ CACHE LOOKUP ────────────────────────────────────────────────────┐" )
506531 c = explain_result ['cache_analysis' ]
507- lines .append (f"│ Entries checked: { c ['cache_entries_checked' ]:<5} Threshold: { c ['similarity_threshold' ]:<6} │" )
532+ # Defensive limits: cache_entries_checked capped at 99999 for display
533+ entries_checked = min (c ['cache_entries_checked' ], 99999 )
534+ lines .append (f"│ Entries checked: { entries_checked :<5} Threshold: { c ['similarity_threshold' ]:<6} │" )
508535 lines .append (f"│ Best similarity: { c ['best_similarity' ]:<6} Would hit: { str (c ['would_hit_cache' ]):<6} │" )
509536 if c ['top_matches' ]:
510537 lines .append ("│ Top matches: │" )
511538 for match in c ['top_matches' ][:3 ]:
512539 sim = match ['similarity' ]
513540 hit = "✓" if match ['would_hit' ] else "✗"
514- # Smart truncation for cached queries
515- cached_query = match ['cached_query' ]
516- if not cached_query .endswith ('...' ) and len (cached_query ) > 45 :
517- cached_query = cached_query [:42 ] + "..."
541+ # Smart truncation for cached queries (limit to 45 chars)
542+ cached_query = truncate (match ['cached_query' ], 45 )
518543 lines .append (f"│ { hit } { sim :.4f} - { cached_query :<45} │" )
519544 lines .append ("└───────────────────────────────────────────────────────────────────┘" )
520545 lines .append ("" )
521546
522547 # RL Agent section
523548 lines .append ("┌─ RL AGENT ────────────────────────────────────────────────────────┐" )
524549 r = explain_result ['rl_agent' ]
525- lines .append (f"│ State: { r ['state' ]:<30} Epsilon: { r .get ('epsilon' , r .get ('exploration_probability' , 0 )):<6} │" )
526- lines .append (f"│ Best action: { r ['best_action' ]:<20} │" )
550+ # Defensive truncation for state (30 chars) and best_action (20 chars)
551+ state_display = truncate (r ['state' ], 30 )
552+ best_action_display = truncate (r ['best_action' ], 20 )
553+ lines .append (f"│ State: { state_display :<30} Epsilon: { r .get ('epsilon' , 0 ):<6} │" )
554+ lines .append (f"│ Best action: { best_action_display :<20} │" )
527555 lines .append ("│ Q-values: │" )
528556 for action , qval in r ['q_values' ].items ():
529- lines .append (f"│ { action :<15} : { qval :>8.4f} │" )
557+ # Truncate action names to 15 chars for alignment
558+ action_display = truncate (action , 15 )
559+ lines .append (f"│ { action_display :<15} : { qval :>8.4f} │" )
530560 lines .append ("└───────────────────────────────────────────────────────────────────┘" )
531561 lines .append ("" )
532562
533563 # Execution strategy
534564 lines .append ("┌─ EXECUTION STRATEGY ──────────────────────────────────────────────┐" )
535565 e = explain_result ['execution_strategy' ]
536- lines .append (f"│ Strategy: { e ['strategy' ]:<20} Est. latency: { e ['estimated_latency' ]:<10} │" )
566+ # Defensive truncation for strategy (20 chars)
567+ strategy_display = truncate (e ['strategy' ], 20 )
568+ recommendation_display = truncate (e ['recommendation' ], 40 )
569+ lines .append (f"│ Strategy: { strategy_display :<20} Est. latency: { e ['estimated_latency' ]:<10} │" )
537570 lines .append (f"│ Will cache: { str (e ['will_cache_result' ]):<8} │" )
538- lines .append (f"│ Recommendation: { e [ 'recommendation' ] :<40} │" )
571+ lines .append (f"│ Recommendation: { recommendation_display :<40} │" )
539572 lines .append ("└───────────────────────────────────────────────────────────────────┘" )
540573
541574 return "\n " .join (lines )
0 commit comments