mirror of
https://github.com/supabase/supabase.git
synced 2026-06-02 02:43:26 +08:00
## Summary Part 4 of the SafeSql migration stack ([#45897](https://github.com/supabase/supabase/pull/45897), [#45903](https://github.com/supabase/supabase/pull/45903), [#45990](https://github.com/supabase/supabase/pull/45990), this PR, …). Converts the remaining reports, query performance, observability, index advisor, and privileges call sites of `executeSql` to produce `SafeSqlFragment` values. The `ReportQuery.sql` field flips from `string` to `SafeSqlFragment`, which cascades into every consumer — landed here atomically so each branch typechecks cleanly. Touched areas: - `interfaces/Reports/*` — `ReportQuery.sql: SafeSqlFragment`, plus all report definitions/utilities updated - `interfaces/QueryPerformance/useQueryPerformanceQuery.ts` - `interfaces/Database/IndexAdvisor/*` and `data/database/{table-index-advisor,retrieve-index-advisor-result}-query.ts` - `data/privileges/{table-api-access,update-exposed-entities}-mutation.ts` - `interfaces/Storage/StoragePolicies/StoragePolicies.tsx` - `hooks/analytics/useDbQuery.tsx` - `Observability/useSlowQueriesCount.ts` + `useQueryInsightsIssues.utils.test.ts` ## Test plan - [x] `pnpm typecheck` passes - [x] `useQueryInsightsIssues.utils.test.ts` passes - [x] Dev-server smoke test: reports pages, query performance, index advisor, storage policies <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Reworked SQL construction and typings across reporting, query performance, index advisor, and privilege features to use safer SQL fragments, improving reliability and preventing query composition issues. * **Types** * Reporting query types were split to distinguish database vs. logs queries, enabling correct handling and validation. * **Docs/Utils** * Added a helper to consistently generate logs SQL for report hooks. * **Tests** * Updated tests to exercise the new SQL-building API. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45998) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
94 lines
2.4 KiB
TypeScript
94 lines
2.4 KiB
TypeScript
import { type SafeSqlFragment } from '@supabase/pg-meta/src/pg-format'
|
|
import { useQuery } from '@tanstack/react-query'
|
|
|
|
import { DEFAULT_QUERY_PARAMS } from '@/components/interfaces/Reports/Reports.constants'
|
|
import {
|
|
BaseReportParams,
|
|
MetaQueryResponse,
|
|
ReportQueryDb,
|
|
} from '@/components/interfaces/Reports/Reports.types'
|
|
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
|
|
import { executeSql } from '@/data/sql/execute-sql-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector'
|
|
|
|
export interface DbQueryHook<T = any> {
|
|
isLoading: boolean
|
|
isRefetching: boolean
|
|
error: string
|
|
data: T[]
|
|
params: BaseReportParams
|
|
logData?: never
|
|
runQuery: () => void
|
|
setParams?: never
|
|
changeQuery?: never
|
|
resolvedSql: string
|
|
}
|
|
|
|
// [Joshen] Atm this is being used only in query performance
|
|
const useDbQuery = ({
|
|
sql,
|
|
params = DEFAULT_QUERY_PARAMS,
|
|
where,
|
|
orderBy,
|
|
}: {
|
|
sql: ReportQueryDb['safeSql'] | SafeSqlFragment
|
|
params?: BaseReportParams
|
|
where?: string
|
|
orderBy?: string
|
|
}): DbQueryHook => {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const state = useDatabaseSelectorStateSnapshot()
|
|
|
|
const { data: databases } = useReadReplicasQuery({ projectRef: project?.ref })
|
|
const connectionString = (databases || []).find(
|
|
(db) => db.identifier === state.selectedDatabaseId
|
|
)?.connectionString
|
|
const identifier = state.selectedDatabaseId
|
|
|
|
const resolvedSql = typeof sql === 'function' ? sql([]) : sql
|
|
|
|
const {
|
|
data,
|
|
error: rqError,
|
|
isPending,
|
|
isRefetching,
|
|
refetch,
|
|
} = useQuery({
|
|
queryKey: [
|
|
'projects',
|
|
project?.ref,
|
|
'db',
|
|
{ ...params, sql: resolvedSql, identifier },
|
|
where,
|
|
orderBy,
|
|
],
|
|
queryFn: ({ signal }) => {
|
|
return executeSql(
|
|
{
|
|
projectRef: project?.ref,
|
|
connectionString: connectionString || project?.connectionString,
|
|
sql: resolvedSql,
|
|
},
|
|
signal
|
|
).then((res) => res.result) as Promise<MetaQueryResponse>
|
|
},
|
|
enabled: Boolean(resolvedSql),
|
|
refetchOnWindowFocus: false,
|
|
refetchOnReconnect: false,
|
|
})
|
|
|
|
const error = rqError || (typeof data === 'object' ? data?.error : '')
|
|
return {
|
|
error,
|
|
data,
|
|
isLoading: isPending,
|
|
isRefetching,
|
|
params,
|
|
runQuery: refetch,
|
|
resolvedSql,
|
|
}
|
|
}
|
|
|
|
export default useDbQuery
|