## Summary
PR 10 of the analytics SQL safety series. Migrates the last surface of
analytics queries that flowed through plain
`get(.../analytics/endpoints/logs.all, { query: { sql } })` or the
`fetchLogs(projectRef, sql: string, ...)` helper over to
`executeAnalyticsSql` with branded `SafeLogSqlFragment` inputs.
After this PR, every analytics SQL call site builds its query through
the safe-analytics-sql helpers and hits the wire through the single
`executeAnalyticsSql` boundary. User-controlled values (filter
operators, numeric thresholds, function IDs, regions, provider names)
all flow through `analyticsLiteral` / branded operator maps; static
fragments are wrapped in `safeSql`. PR 11 (ESLint / vitest rule
forbidding direct analytics-endpoint POST/GET outside
`executeAnalyticsSql`) is the next and final step.
## Changes
- **`hooks/analytics/useProjectUsageStats.tsx`** — route the
already-branded `genChartQuery` output through `executeAnalyticsSql`
(parallels `useLogsPreview`).
- **`data/reports/report.utils.ts`** — tighten `fetchLogs(sql)` from
`string` to `SafeLogSqlFragment`; the wire boundary is now the same
single `executeAnalyticsSql` wrapper used by the rest of the analytics
path. Adds two pre-branded fragment maps reused by the report configs:
- `SAFE_GRANULARITY_SQL` — closed set returned by
`analyticsIntervalToGranularity`.
- `SAFE_COMPARISON_OPERATOR_SQL` — closed set on
`NumericFilter.operator`.
- **`components/interfaces/Auth/Overview/OverviewErrors.constants.ts`**
— wrap the two static `AUTH_TOP_*_SQL` fragments in `safeSql` (no
interpolation, but the type now flows).
- **`data/reports/v2/edge-functions.config.ts`** — `filterToWhereClause`
and every entry in `METRIC_SQL` now return `SafeLogSqlFragment`.
User-controlled values (`status_code.value`, `execution_time.value`,
function IDs, regions) pass through `analyticsLiteral`; operators look
up the branded map; the granularity uses the branded map. The
wire-format strings are unchanged, so the existing
`edge-functions.test.tsx` exact-string expectations still hold.
- **`data/reports/v2/auth.config.ts`** — same shape applied to all ten
`AUTH_REPORT_SQL` entries. The legacy `whereClause.replace(/^WHERE\s+/,
'')` pattern is replaced by two helpers that emit `AND`-prefixed
predicate fragments directly (`authFiltersToAndPredicates`,
`edgeLogsFiltersToAndPredicates`). Static provider SELECT / GROUP BY
fragments are pre-branded.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Enhanced security for analytics and reporting queries by updating
query construction methods across auth, edge functions, and project
usage reports.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46476?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 -->
## Changes
- Replaces plan ID-based chart gating with entitlement-based checks
across all observability pages (database, auth, realtime,
edge-functions)
- Adds requiredPlan display field so the upsell shows the correct target
plan (e.g. "Upgrade to Team" for disk throughput, not just "Upgrade")
- Decouples database-charts.ts from Organization Plan, now accepts
pre-computed entitlement flags instead
### Note on `requiredPlan`
The entitlement system currently tells us whether a user has access, but
not what plan they need to get access. `requiredPlan` is a frontend-only
display hint. Ideally the backend entitlement response would include a
minimumPlan field so we can remove this hardcoded mapping. For now, this
is a pragmatic workaround to avoid misleading upsell copy (e.g. a Free
user seeing "Upgrade" for a Team-only feature and landing on Pro).
## Testing
### Database
- Head to `/project/_/observability/database` with an Org on the Free
Plan and assert that the Disk throughput is gated.
<img width="1402" height="682" alt="image"
src="https://github.com/user-attachments/assets/b783d866-774d-44dc-88d1-797d26502f02"
/>
### Auth
- Head to `/project/_/observability/auth` with an Org on the Free Plan
and assert that the following metrics are gated:
<img width="1402" height="682" alt="image"
src="https://github.com/user-attachments/assets/cb062dd7-5864-4e40-a11f-ad6be1f7fd41"
/>
### Edge Functions
- Head to `/project/_/observability/edge-functions` with an Org on the
Free Plan and assert that the following metrics are gated:
<img width="1402" height="320" alt="image"
src="https://github.com/user-attachments/assets/517b5483-4575-493c-9401-2c67ac9e684c"
/>
### Realtime
- Head to `/project/_/observability/realtime` with an Org on the Free
Plan and assert that the following metrics are gated:
<img width="1402" height="722" alt="image"
src="https://github.com/user-attachments/assets/0c2757a2-8482-43f4-a65c-c0dde3d878c7"
/>