useSupamonitorIndexAdvisor.ts62 lines · main
1import { useQueries } from '@tanstack/react-query'
2
3import { useIndexAdvisorStatus } from '../../QueryPerformance/hooks/useIsIndexAdvisorStatus'
4import type { QueryPerformanceRow } from '../../QueryPerformance/QueryPerformance.types'
5import { databaseKeys } from '@/data/database/keys'
6import { getIndexAdvisorResult } from '@/data/database/retrieve-index-advisor-result-query'
7import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
8
9function isEligibleQuery(query: string): boolean {
10 const lower = query.trim().toLowerCase()
11 if (!lower.startsWith('select') && !lower.startsWith('with')) return false
12 // Dollar-quoted string literals (e.g. $$...$$ or $tag$...$tag$) break the single-quoted
13 // SQL embedding in getIndexAdvisorResult. Plain parameter markers ($1, $2, …) are fine.
14 if (/\$([A-Za-z_][A-Za-z0-9_]*)?\$/.test(query)) return false
15 return true
16}
17
18export function useSupamonitorIndexAdvisor(rows: QueryPerformanceRow[]): QueryPerformanceRow[] {
19 const { data: project } = useSelectedProjectQuery()
20 const { isIndexAdvisorEnabled } = useIndexAdvisorStatus()
21
22 const eligibleQueries = rows.map((r) => r.query).filter(isEligibleQuery)
23
24 const results = useQueries({
25 queries: eligibleQueries.map((query) => ({
26 queryKey: databaseKeys.indexAdvisorFromQuery(
27 project?.ref,
28 query,
29 project?.connectionString ?? undefined
30 ),
31 queryFn: () =>
32 getIndexAdvisorResult({
33 projectRef: project?.ref,
34 connectionString: project?.connectionString,
35 query,
36 }),
37 enabled: isIndexAdvisorEnabled && !!project?.ref,
38 retry: false,
39 staleTime: Infinity,
40 })),
41 })
42
43 if (!isIndexAdvisorEnabled) return rows
44
45 const resultByQuery = new Map(
46 eligibleQueries.map((query, i) => {
47 const result = results[i]
48 // Only treat as ready when status is definitively success or error.
49 // undefined/pending means data hasn't arrived yet — store undefined so
50 // classifyQuery can defer slow classification and avoid flicker.
51 const isReady = result?.status === 'success' || result?.status === 'error'
52 return [query, isReady ? (result.data ?? null) : undefined]
53 })
54 )
55
56 return rows.map((row) => ({
57 ...row,
58 index_advisor_result: resultByQuery.has(row.query)
59 ? resultByQuery.get(row.query)
60 : (row.index_advisor_result ?? null),
61 }))
62}