mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
fix: updated explain to not crash on non text formats (#42076)
updated explain to not crash on non text formats
This commit is contained in:
1 parent
d27bb74258
commit
f4409d4ac5
3 files changed
+60
-1
No files matched your search
@@ -133,6 +133,12 @@ export function isExplainQuery(rows: readonly unknown[]): boolean {
|
||||
return 'QUERY PLAN' in firstRow && Object.keys(firstRow).length === 1
|
||||
}
|
||||
|
||||
export function isTextFormatExplain(rows: readonly unknown[]): boolean {
|
||||
if (!isExplainQuery(rows)) return false
|
||||
const firstRow = rows[0] as Record<string, unknown>
|
||||
return typeof firstRow['QUERY PLAN'] === 'string'
|
||||
}
|
||||
|
||||
export function isExplainSql(sql: string): boolean {
|
||||
return /^\s*explain\b/i.test(sql)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import { useState } from 'react'
|
||||
import CopyButton from 'components/ui/CopyButton'
|
||||
import { ExplainVisualizer } from 'components/interfaces/ExplainVisualizer/ExplainVisualizer'
|
||||
import { ExplainHeader } from 'components/interfaces/ExplainVisualizer/ExplainVisualizer.Header'
|
||||
import { isExplainQuery } from 'components/interfaces/ExplainVisualizer/ExplainVisualizer.utils'
|
||||
import {
|
||||
isExplainQuery,
|
||||
isTextFormatExplain,
|
||||
} from 'components/interfaces/ExplainVisualizer/ExplainVisualizer.utils'
|
||||
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
import Results from './Results'
|
||||
@@ -78,6 +81,7 @@ export function UtilityTabExplain({ id, isExecuting }: UtilityTabExplainProps) {
|
||||
}
|
||||
|
||||
const isValidExplain = isExplainQuery(explainResult.rows)
|
||||
const isTextFormat = isTextFormatExplain(explainResult.rows)
|
||||
|
||||
if (!isValidExplain) {
|
||||
return (
|
||||
@@ -89,6 +93,20 @@ export function UtilityTabExplain({ id, isExecuting }: UtilityTabExplainProps) {
|
||||
)
|
||||
}
|
||||
|
||||
// Handle non-TEXT formats (JSON, YAML, XML) - show raw output only
|
||||
if (!isTextFormat) {
|
||||
return (
|
||||
<div className="h-full flex flex-col pb-9">
|
||||
<div className="px-4 py-3 bg-surface-100 border-b border-default flex items-center gap-2">
|
||||
<span className="text-sm text-foreground-light">
|
||||
Visual execution plan is only available for TEXT format. Showing raw output.
|
||||
</span>
|
||||
</div>
|
||||
<Results rows={explainResult.rows} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const toggleMode = () => setMode(mode === 'visual' ? 'raw' : 'visual')
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
splitSqlStatements,
|
||||
isExplainQuery,
|
||||
isExplainSql,
|
||||
isTextFormatExplain,
|
||||
formatNodeDuration,
|
||||
} from 'components/interfaces/ExplainVisualizer/ExplainVisualizer.utils'
|
||||
|
||||
@@ -12,6 +13,12 @@ describe('isExplainQuery', () => {
|
||||
expect(isExplainQuery(rows)).toBe(true)
|
||||
})
|
||||
|
||||
test('returns true for JSON format EXPLAIN result rows', () => {
|
||||
// JSON format returns an array/object in the QUERY PLAN column
|
||||
const rows = [{ 'QUERY PLAN': [{ Plan: { 'Node Type': 'Seq Scan' } }] }]
|
||||
expect(isExplainQuery(rows)).toBe(true)
|
||||
})
|
||||
|
||||
test('returns false for empty array', () => {
|
||||
expect(isExplainQuery([])).toBe(false)
|
||||
})
|
||||
@@ -22,6 +29,34 @@ describe('isExplainQuery', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('isTextFormatExplain', () => {
|
||||
test('returns true for TEXT format EXPLAIN result rows', () => {
|
||||
const rows = [
|
||||
{ 'QUERY PLAN': 'Seq Scan on users (cost=0.00..10.50 rows=100 width=36)' },
|
||||
{ 'QUERY PLAN': ' Filter: (active = true)' },
|
||||
]
|
||||
expect(isTextFormatExplain(rows)).toBe(true)
|
||||
})
|
||||
|
||||
test('returns false for JSON format EXPLAIN result rows', () => {
|
||||
const rows = [
|
||||
{ 'QUERY PLAN': [{ Plan: { 'Node Type': 'Seq Scan', 'Relation Name': 'users' } }] },
|
||||
]
|
||||
expect(isTextFormatExplain(rows)).toBe(false)
|
||||
})
|
||||
|
||||
test('returns true for YAML format EXPLAIN result rows (returned as string)', () => {
|
||||
const rows = [
|
||||
{
|
||||
'QUERY PLAN':
|
||||
'- Plan: \n Node Type: "Seq Scan"\n Parallel Aware: false\n Async Capable: false\n Relation Name: "orders"\n Alias: "orders"\n Startup Cost: 0.00\n Total Cost: 97.00\n Plan Rows: 5000\n Plan Width: 41',
|
||||
},
|
||||
]
|
||||
|
||||
expect(isTextFormatExplain(rows)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isExplainSql', () => {
|
||||
test('returns true for EXPLAIN queries', () => {
|
||||
expect(isExplainSql('EXPLAIN SELECT * FROM users')).toBe(true)
|
||||
|
||||
Reference in new issue
Block a user