Skip to content

Latest commit

 

History

History
306 lines (231 loc) · 7.68 KB

File metadata and controls

306 lines (231 loc) · 7.68 KB

Performance UI - Bug Fixes

Date: December 24, 2025 Status: ✅ FIXED


Issues Identified and Fixed

Issue 1: estimatedCostMs Field Name Mismatch ❌ → ✅

Error:

Cannot read properties of undefined (reading 'toFixed')
at ExplainPlanTab.js:281

Root Cause:

  • Backend model uses estimatedCost (without "Ms" suffix)
  • Frontend was trying to access analysis.estimatedCostMs
  • Field was undefined, causing .toFixed(2) to fail

Fix:

  • Changed analysis.estimatedCostMs to analysis.estimatedCost
  • Added proper null/undefined checks
  • File: src/components/tabs/ExplainPlanTab.js:278-282

Before:

{analysis.estimatedCostMs !== null && (
    <span className={styles.metricValue}>{analysis.estimatedCostMs.toFixed(2)}ms</span>
)}

After:

{analysis.estimatedCost !== null && analysis.estimatedCost !== undefined && (
    <span className={styles.metricValue}>{analysis.estimatedCost.toFixed(2)}</span>
)}

Issue 2: planNodes vs planTree Field Name Mismatch ❌ → ✅

Root Cause:

  • Backend model returns planTree (single ExplainPlanNode object)
  • Frontend was expecting planNodes (array)

Fix:

  • Changed analysis.planNodes to analysis.planTree
  • Updated rendering logic to handle single node instead of array
  • File: src/components/tabs/ExplainPlanTab.js:355-363

Before:

{analysis.planNodes && analysis.planNodes.length > 0 ? (
    analysis.planNodes.map((node, idx) => renderPlanNode(node, idx))
) : (
    <div>No execution plan nodes available</div>
)}

After:

{analysis.planTree ? (
    renderPlanNode(analysis.planTree, 0)
) : (
    <div>No execution plan available</div>
)}

Issue 3: Missing Null/Undefined Checks ❌ → ✅

Root Cause:

  • JavaScript treats null !== undefined as true
  • Checks like value !== null don't catch undefined values
  • Caused runtime errors when calling methods on undefined

Fixes Applied:

ExplainPlanTab.js

  1. Line 272-276: estimatedRows check

    // Added undefined check
    {analysis.estimatedRows !== null && analysis.estimatedRows !== undefined && (
        <span>{analysis.estimatedRows.toLocaleString()}</span>
    )}
  2. Line 278-282: estimatedCost check (also fixed field name)

    {analysis.estimatedCost !== null && analysis.estimatedCost !== undefined && (
        <span>{analysis.estimatedCost.toFixed(2)}</span>
    )}
  3. Line 320-324: affectedRows check

    {issue.affectedRows !== null && issue.affectedRows !== undefined && issue.affectedRows > 0 && (
        <div>Affected Rows: {issue.affectedRows.toLocaleString()}</div>
    )}

SlowQueryAnalysisTab.js

  1. Line 438-440: efficiencyRatio check
    // Added ternary to handle undefined
    {query.efficiencyRatio !== null && query.efficiencyRatio !== undefined
        ? `${(query.efficiencyRatio * 100).toFixed(4)}%`
        : 'N/A'}

Backend Model Reference

ExplainPlanAnalysis.java Fields

The actual backend fields (from ExplainPlanAnalysis.java):

// What the backend ACTUALLY returns:
private Double estimatedCost;      // NOT estimatedCostMs
private Double actualCost;
private Double estimatedRows;
private Long actualRows;
private ExplainPlanNode planTree;  // NOT planNodes (single node, not array)

Testing After Fixes

Test the EXPLAIN Plan Tab

  1. Navigate to http://localhost:3000
  2. Select a database connection
  3. Click the ⚡ EXPLAIN Plan tab
  4. Enter this query:
    SELECT * FROM users WHERE id = 1;
  5. Click "Analyze Query"

Expected Results:

  • ✅ No runtime errors
  • ✅ Performance score displays correctly
  • ✅ Estimated Cost shows (if available) or is hidden
  • ✅ Estimated Rows shows (if available) or is hidden
  • ✅ Execution plan tree displays
  • ✅ Issues display with severity badges
  • ✅ AI summary appears

Test with EXPLAIN ANALYZE

  1. Check the "Use EXPLAIN ANALYZE" checkbox
  2. Click "Analyze Query"

Expected Results:

  • ✅ Query executes successfully
  • ✅ Additional metrics appear (actual rows, actual cost)
  • ✅ No errors with undefined fields

Additional Defensive Programming

Pattern Used

For all optional numeric fields that need method calls (.toFixed(), .toLocaleString()):

// Pattern 1: Conditional rendering
{value !== null && value !== undefined && (
    <span>{value.toFixed(2)}</span>
)}

// Pattern 2: Ternary with fallback
{value !== null && value !== undefined
    ? `${value.toFixed(2)}%`
    : 'N/A'}

// Pattern 3: Optional chaining (cleaner, modern)
{value?.toLocaleString()}  // Safe, returns undefined if value is null/undefined

Why Both Null and Undefined?

In JavaScript:

null !== undefined  // true ⚠️
null !== null       // false ✅
undefined !== undefined  // false ✅

// So checking only !== null is NOT enough:
let value = undefined
if (value !== null) {
    value.toFixed(2)  // ❌ ERROR: Cannot read property 'toFixed' of undefined
}

// Need to check both:
if (value !== null && value !== undefined) {
    value.toFixed(2)  // ✅ Safe
}

Files Modified

  1. src/components/tabs/ExplainPlanTab.js

    • Line 56-58: Fixed planTree auto-expand
    • Line 272-282: Fixed estimatedRows and estimatedCost checks
    • Line 320-324: Fixed affectedRows check
    • Line 355-363: Fixed planTree rendering
  2. src/components/tabs/SlowQueryAnalysisTab.js

    • Line 438-440: Fixed efficiencyRatio check

Lessons Learned

1. Always Match Frontend/Backend Field Names

  • Backend: estimatedCost → Frontend: estimatedCost
  • Backend: planTree → Frontend: planTree
  • Keep naming consistent to avoid confusion

2. Understand Data Structure

  • Backend returns single node (planTree), not array (planNodes)
  • Check model files before writing frontend code
  • Consider adding TypeScript for type safety

3. Defensive Programming

  • Always check for both null and undefined
  • Use optional chaining (?.) where possible
  • Provide fallback values (e.g., 'N/A')

4. Test with Real Data

  • API might return partial data
  • Fields can be null/undefined even if documented
  • Always handle missing fields gracefully

Next Steps

Recommended Improvements

  1. Add TypeScript (Future)

    • Define interfaces matching backend models
    • Catch field name mismatches at compile time
    • Get autocomplete for API responses
  2. Add Loading Skeletons

    • Show placeholder content while loading
    • Better UX than blank screen
    • Prevents layout shift
  3. Add Error Boundary

    • Catch unexpected runtime errors
    • Show friendly error message
    • Prevent entire app crash
  4. API Response Validation

    • Validate API response structure
    • Log warnings for missing expected fields
    • Easier debugging in production

Example TypeScript Interface

interface ExplainPlanAnalysis {
    connectionId: string
    query: string
    planTree?: ExplainPlanNode  // Single node, optional
    issues: PerformanceIssue[]
    estimatedCost?: number       // Not estimatedCostMs!
    estimatedRows?: number
    performanceScore: number
    aiSummary?: string
    // ... other fields
}

Summary

Total Issues Fixed: 4 Files Modified: 2 Lines Changed: ~15

Status: ✅ All runtime errors resolved Ready for Testing: ✅ YES

The EXPLAIN Plan and Slow Query Analysis tabs should now work correctly without runtime errors. All undefined field access issues have been fixed with proper null/undefined checks and correct field names matching the backend model.


Test Now: Open http://localhost:3000 and try the ⚡ EXPLAIN Plan tab!