mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 20:05:06 +03: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 -->
70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
/**
|
|
* Wire-boundary for analytics SQL execution.
|
|
*
|
|
* This is the analytics-path analog of pg-meta's `executeSql`. It accepts only
|
|
* `SafeLogSqlFragment` — plain strings are rejected at compile time — so any
|
|
* value flowing from URL parameters, UI inputs, or LLM output must pass through
|
|
* a sanitization helper in safe-analytics-sql.ts before reaching the wire.
|
|
*
|
|
* See .claude/skills/safe-sql-execution/SKILL.md for the full security model.
|
|
*/
|
|
import type { SafeLogSqlFragment } from './safe-analytics-sql'
|
|
import { get, handleError, post } from '@/data/fetchers'
|
|
|
|
/**
|
|
* Analytics endpoints that accept `{ sql, iso_timestamp_start, iso_timestamp_end }`
|
|
* either as a POST body or GET query string. Extend this union as additional
|
|
* endpoints are migrated to the safe-analytics-sql pattern.
|
|
*/
|
|
export type AnalyticsSqlEndpoint =
|
|
| '/platform/projects/{ref}/analytics/endpoints/logs.all'
|
|
| '/platform/projects/{ref}/analytics/endpoints/logs.all.otel'
|
|
|
|
export interface ExecuteAnalyticsSqlVariables {
|
|
projectRef: string
|
|
endpoint: AnalyticsSqlEndpoint
|
|
/** Must carry the `SafeLogSqlFragment` brand — plain strings are rejected at compile time. */
|
|
sql: SafeLogSqlFragment
|
|
iso_timestamp_start: string
|
|
iso_timestamp_end: string
|
|
/** Defaults to 'post'. Use 'get' to preserve wire behavior when migrating legacy GET callers. */
|
|
method?: 'get' | 'post'
|
|
signal?: AbortSignal
|
|
headers?: HeadersInit
|
|
}
|
|
|
|
export async function executeAnalyticsSql({
|
|
projectRef,
|
|
endpoint,
|
|
sql,
|
|
iso_timestamp_start,
|
|
iso_timestamp_end,
|
|
method = 'post',
|
|
signal,
|
|
headers: headersInit,
|
|
}: ExecuteAnalyticsSqlVariables) {
|
|
const headers = headersInit !== undefined ? new Headers(headersInit) : undefined
|
|
|
|
if (method === 'get') {
|
|
const { data, error } = await get(endpoint, {
|
|
params: {
|
|
path: { ref: projectRef },
|
|
query: { sql, iso_timestamp_start, iso_timestamp_end },
|
|
},
|
|
signal,
|
|
headers,
|
|
})
|
|
if (error) handleError(error)
|
|
return data
|
|
}
|
|
|
|
const { data, error } = await post(endpoint, {
|
|
params: { path: { ref: projectRef } },
|
|
body: { sql, iso_timestamp_start, iso_timestamp_end },
|
|
signal,
|
|
headers,
|
|
})
|
|
if (error) handleError(error)
|
|
return data
|
|
}
|