mirror of
https://github.com/supabase/supabase.git
synced 2026-06-05 04:12:29 +08:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Refactor / type safety improvement ## What is the current behavior? The legacy log query stack (`genDefaultQuery`, `genCountQuery`, `genChartQuery`, `genWhereStatement`, `useLogsPreview`, `useSingleLog`) builds SQL from raw strings with no type-level guarantee that values are safely interpolated. Identifier helpers (`bqIdent`, `bqDottedIdent`, `clickhouseIdent`, `clickhouseDottedIdent`) are duplicated across BigQuery and ClickHouse variants, and `bqDottedIdent` wraps the entire dotted path in one backtick pair (`` `request.pathname` ``), which BigQuery treats as a literal column name rather than a UNNEST alias field — causing runtime query failures on dotted filter keys. ## What is the new behavior? - All gen functions return `SafeLogSqlFragment` and all callers route through `executeAnalyticsSql`, enforcing compile-time SQL provenance tracking across the legacy stack. - `bqIdent` / `bqDottedIdent` / `clickhouseIdent` / `clickhouseDottedIdent` are replaced by a single `quotedIdent` function that backtick-quotes each segment individually (e.g. `` `request`.`pathname` ``). ClickHouse natively accepts backticks, so one function serves both engines and the dotted-path quoting bug is fixed. - `SQL_FILTER_TEMPLATES` entries are converted to `SafeLogSqlFragment` (static via `safeSql`, dynamic via `safeSql` + `analyticsLiteral`). - `buildWhereClauses` is extracted as a private helper returning `SafeLogSqlFragment[]` so the pg_cron path can merge clauses without unsafe slice-and-cast. ## Additional context <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Logs query generation migrated to safer, engine-agnostic SQL fragments, typed filter templates, and unified identifier quoting for stronger injection protection and more consistent queries. * Logs preview and single-log retrieval now execute analytics SQL end-to-end using the unified executor. * **New Features** * Analytics SQL executor can call the backend via GET or POST and accepts method selection. * **Tests** * Updated tests to validate unified identifier quoting and safe-SQL helper behavior. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46351?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
|
|
import { LOGS_TABLES } from '@/components/interfaces/Settings/Logs/Logs.constants'
|
|
import type {
|
|
LogData,
|
|
Logs,
|
|
LogsEndpointParams,
|
|
QueryType,
|
|
} from '@/components/interfaces/Settings/Logs/Logs.types'
|
|
import { genSingleLogQuery } from '@/components/interfaces/Settings/Logs/Logs.utils'
|
|
import { executeAnalyticsSql } from '@/data/logs/execute-analytics-sql'
|
|
import { safeSql } from '@/data/logs/safe-analytics-sql'
|
|
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
|
|
|
interface SingleLogHook {
|
|
data: LogData | undefined
|
|
error: string | Object | null
|
|
isLoading: boolean
|
|
refresh: () => void
|
|
}
|
|
|
|
type SingleLogParams = {
|
|
id?: string
|
|
projectRef: string
|
|
queryType?: QueryType
|
|
paramsToMerge?: Partial<LogsEndpointParams>
|
|
}
|
|
function useSingleLog({
|
|
projectRef,
|
|
id,
|
|
queryType,
|
|
paramsToMerge,
|
|
}: SingleLogParams): SingleLogHook {
|
|
const table = queryType ? LOGS_TABLES[queryType] : undefined
|
|
const sql = id && table ? genSingleLogQuery(table, id) : safeSql``
|
|
|
|
const enabled = Boolean(id && table)
|
|
|
|
const { logsMetadata } = useIsFeatureEnabled(['logs:metadata'])
|
|
|
|
const {
|
|
data,
|
|
error: rcError,
|
|
isPending,
|
|
isRefetching,
|
|
refetch,
|
|
} = useQuery({
|
|
// id and queryType uniquely identify sql without having to stick the
|
|
// entire sql in the query key.
|
|
// eslint-disable-next-line @tanstack/query/exhaustive-deps
|
|
queryKey: [
|
|
'projects',
|
|
projectRef,
|
|
'single-log',
|
|
id,
|
|
queryType,
|
|
paramsToMerge?.iso_timestamp_start,
|
|
paramsToMerge?.iso_timestamp_end,
|
|
],
|
|
queryFn: async ({ signal }) => {
|
|
const data = await executeAnalyticsSql({
|
|
projectRef,
|
|
endpoint: '/platform/projects/{ref}/analytics/endpoints/logs.all',
|
|
sql,
|
|
iso_timestamp_start: paramsToMerge?.iso_timestamp_start ?? '',
|
|
iso_timestamp_end: paramsToMerge?.iso_timestamp_end ?? '',
|
|
method: 'get',
|
|
signal,
|
|
})
|
|
return data as unknown as Logs
|
|
},
|
|
enabled,
|
|
refetchOnWindowFocus: false,
|
|
refetchOnMount: false,
|
|
refetchOnReconnect: false,
|
|
})
|
|
|
|
let error: null | string | object = rcError ? (rcError as any).message : null
|
|
const result = data?.result ? data.result[0] : undefined
|
|
|
|
return {
|
|
data: !!result
|
|
? { ...result, metadata: logsMetadata ? result?.metadata : undefined }
|
|
: undefined,
|
|
isLoading: (enabled && isPending) || isRefetching,
|
|
error,
|
|
refresh: () => refetch(),
|
|
}
|
|
}
|
|
export default useSingleLog
|