Commit Graph
9 Commits
Author SHA1 Message Date
Joshen Lim 71d58cba7f Joshenlim/fe 4401 re sql editor silently points to the primary instead of (#50513)
## Context

Fixes the following 2 issues with the database selection in the SQL
Editor
- An errant `useEffect` was resetting the `selectedDatabaseId` back to
the primary every time the `databases` list from `useReadReplicasQuery`
changed reference (not just on first load).
- `QuerySourceMenu` kept showing "Read Replica" even after selection had
reverted
- Was using local storage value as the `identifier` for
`DatabaseParametersSubMenu`, when it should use the valtio store as the
source of truth

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Improvements**
- The SQL Editor now remembers the last selected database between
sessions.
- Your saved database selection is restored when available; otherwise,
the project’s primary database is selected automatically.
- Query source settings now stay synchronized with the database
currently selected in the SQL Editor.

- **Bug Fixes**
- Background database refreshes no longer unexpectedly reset your
selected read replica to the primary database.
- Database selection now waits for saved preferences to load, preventing
a brief incorrect selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-17 12:34:13 +00:00
Charis 0ed49231b7 refactor(studio): unify CellSource and the SQL editor's QuerySource into QuerySourceBinding (#49072)
Third of a stack. **Stacked on #49070** (which is stacked on #49069) —
review those first. Base retargets automatically as each merges.

Mechanical throughout; no behavior change.

## The problem

Three types described where a query runs, and no two agreed:

| | shape |
|---|---|
| `CellSource` (registry) | `{ id, type, parameters: { … } }` — `id` and
`type` always held the same literal |
| `QuerySource` (SQL editor) | `{ type: 'database' } \| { type: 'logs',
dateRange }` |
| notebook cells | flat per-backend fields, neither of the above |

Anything crossing between them needed a translation that dropped fields
on the way — which is how a notebook cell's replica selection had
nowhere to go.

## What changed

One `QuerySourceBinding`: a backend `_tag` with that backend's
parameters spread flat beside it, borrowed from the wire schema (#49069)
so the binding and the persisted cell agree by construction.

- **`QuerySource` is deleted.** `useRunSource` returns the shared
binding, so `runSource.type`/`dateRange` become `_tag`/`time_range`
across the SQL editor — that is most of the file count here.
- **`getQuerySourceBinding`** projects a notebook cell onto a binding;
**`toQuerySourceBinding`** does the same for any backend-tagged carrier.
Both overloaded so an already-narrowed caller gets the matching binding
back rather than the union, which keeps the result spreadable without
re-narrowing.
- **`ExplorerQuerySourceMenu`** drops its inline copy of the
custom-range and upgrade-prompt logic in favor of `useLogsCustomRange`,
which the SQL editor menu already used.

The registry keeps only what is genuinely runtime: endpoints, labels,
icons, availability, defaults. What a query *is* stays in the wire
schema.

## Verification

Typecheck, Prettier, and the lint ratchet clean. 405 tests pass across
the notebook schema, query sources, the logs components, the SQL editor,
and the Explorer surfaces.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Improvements**
* Updated query source handling across Explorer and SQL Editor for a
more consistent selection experience.
* Database and log sources now preserve identifiers and time ranges more
reliably when switching or editing queries.
* Source menus, labels, icons, validation, and query execution now
reflect the selected source more accurately.

* **Bug Fixes**
* Invalid or outdated saved source settings now safely fall back to a
database source.
* Improved log-source detection and time-range handling throughout query
editing and execution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-14 15:09:29 +07:00
Charis 8b38e0d1ed feat(studio): ClickHouse dialect for logs snippet AI + rewrite to ClickHouse (#48501)
## 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?

Feature, plus a refactor of the shared logs-rewrite flow.

PR 8 of the SQL editor query-source series. Stacked on #48457 — review
that one first, and merge this after it.

## What is the current behavior?

A `log_sql` snippet runs against the ClickHouse-backed analytics
endpoint, but the SQL editor's AI still writes Postgres: inline edits
get Postgres system prompts, and the result is run through
`sql-formatter`, which mangles ClickHouse backticks and `log_attributes`
map lookups.

Legacy Logs Explorer saved queries open in the editor as `log_sql`
snippets. Those are BigQuery dialect and error against the ClickHouse
endpoint the editor runs them on, with no in-editor way out — only the
Logs Explorer offered a rewrite.

The completion route was also asymmetric. It assembled a
schema/code/instruction message for Postgres but forwarded `prompt`
verbatim for ClickHouse, so a client wanting ClickHouse had to
hand-build the equivalent string.

## What is the new behavior?

**Inline AI speaks ClickHouse for logs snippets.** `sqlSourceToDialect`
maps a snippet's source to `postgres`/`clickhouse` and
`buildCompletionRequestBody` threads it through. For ClickHouse,
`useSqlEditorAi` strips code fences from the response and skips
`formatSql`. Execution and dialect both follow the snippet type, so a
snippet's valid dialect never flips.

**Rewrite to ClickHouse in the editor.** A banner offers the rewrite for
a logs snippet whose text trips `looksLikeLegacyLogsQuery`, and proposes
the result through the editor's existing AI diff view rather than
replacing the snippet, so it's accepted or discarded like any other AI
edit. Gated on `otelLegacyLogs`: on a non-migrated org the BigQuery text
is still correct, so rewriting it would break a working query.

The offer is a state machine (`offered` / `rewriting` / `failed` /
`noRewriteNeeded` / `dismissed`) with a declarative table of valid
transitions, so the states are mutually exclusive by construction and
dismissal is terminal. A failure keeps its message and offers a retry; a
response identical to the input is reported rather than opening an empty
diff.

**One place assembles completion prompts.** The route now uses a single
template for both dialects, branching only the schema section and — for
`intent: 'rewrite'` — the instruction. `lib/ai/clickhouse-logs.ts` is
the single home for ClickHouse-logs prompt content, replacing two
independently maintained descriptions of the same table. Clients carry
no prompt text.

**The rewrite flow is shared with the Logs Explorer.** Both surfaces
previously hand-rolled the same sequence and had drifted: only one
detected a no-op rewrite, they sourced `log_attributes` keys
differently, and the Explorer formatted errors with an `as Error` cast.
Both now use `useLegacyLogsRewrite` and the same state-driven banner, so
the Explorer picks up no-op detection and typed error extraction.

**Attribute keys are fetched on submit, not while typing.** The detected
source would otherwise feed a reactive query key, making every edit that
changed it cost another network call. `useLogsAttributeKeys` is
imperative and goes through `queryClient.fetchQuery`, so a source
already cached — including by the Explorer header and query panel, which
subscribe reactively — is reused. This also closes a gap where inline
edits never received keys at all, unlike full rewrites.

`getErrorMessage` gains an optional typed fallback and no longer
stringifies a bare object into `'[object Object]'`; every existing
caller already hand-rolled a fallback, except `QueueSettings`, which
interpolated the raw result and now passes one.

Nothing here is user-visible until the `sqlEditorLogsSource` flag is
enabled.

Tests: dialect selection and request-body shape, the ClickHouse prompt
content (including that the schema section does not restate the dialect
rules), the reducer's valid and invalid transitions,
`shouldOfferLegacyLogsRewrite`, on-submit key discovery with cache
reuse, and `getErrorMessage`.

## Additional context

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added an Assistant banner to help rewrite legacy BigQuery-style logs
queries into ClickHouse SQL.
* SQL assistance now adapts to the selected query type, including
relevant log attribute context.
* Rewrite suggestions can be reviewed as editor diffs before being
applied.

* **Bug Fixes**
* Improved rewrite failure handling, retry options, dismissal behavior,
and “no rewrite needed” messaging.
* Error notifications now provide a clearer fallback message when
details are unavailable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 09:02:40 -04:00
Charis 50e1eb7436 chore(eslint): bump eslint-config-next to v16 for useEffectEvent (#48458)
## 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?

Chore / build (ESLint config upgrade + lint cleanup).

## What is the current behavior?

`eslint-plugin-react-hooks` v5 (pulled in transitively by
`eslint-config-next` v15) doesn't recognize stable `useEffectEvent`, so
every effect that calls an effect-event handler needs an `eslint-disable
react-hooks/exhaustive-deps` to silence a false positive. There are 30
such dead disables across Studio.

## What is the new behavior?

Bumps `eslint-config-next` to v16, which pulls in
`eslint-plugin-react-hooks` v7 whose `exhaustive-deps` understands
`useEffectEvent`, and removes the 30 now-dead disable directives (and
their orphaned explanatory comments).

Supporting changes:

- **Flat-config migration**: v16 is a native flat-config array (v15 was
eslintrc), so `eslint-config-supabase` now spreads it directly instead
of bridging through `FlatCompat`.
- **React Compiler rules off**: v16 enables react-hooks v7's
`recommended`, which layers the React Compiler lint rules on top of the
two classic rules. These are switched off (derived dynamically from what
next enables) to keep this change scoped to the `exhaustive-deps`
improvement.
- **Plugin-registration fallout** (v16 scopes plugin registration to a
file glob rather than registering globally like FlatCompat did): stop
re-registering `@typescript-eslint` (shared) and `jsx-a11y` (studio);
scope our react / react-hooks / jsx-a11y rule overrides (studio, www) to
v16's plugin glob so they don't error on files outside it (e.g. `.cjs`).
- **Lint surface preserved**: v16's glob newly includes `.mts`/`.cts`
(v15 didn't lint them), which surfaced pre-existing errors in tooling
scripts. The shared config keeps the prior surface by leaving
`.mts`/`.cts` unlinted; linting them is left as a separate change.
- **Ratchet**: rebaselines `@tanstack/query/exhaustive-deps` 9 → 89. v15
forced next's `@babel/eslint-parser` onto `.ts` files, hiding these
deps; v16 parses `.ts` with `@typescript-eslint/parser` and correctly
surfaces the intentional `connectionString`-excluded-from-`queryKey`
pattern. Worth a follow-up to review whether any are real
cache-correctness bugs.
- Drops three now-dead devDeps from `eslint-config-supabase`:
`@eslint/eslintrc`, `@eslint/js`, `@typescript-eslint/eslint-plugin`.

Verified locally: `turbo run lint` → 7/7 packages pass with 0 errors;
Studio `lint:ratchet` passes; Prettier clean on changed files; typecheck
unaffected.

## Additional context

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Refined linting configuration and removed outdated lint suppressions
across Studio.
* Updated Next.js linting support and refreshed related development
configuration.
  * Expanded lint baseline coverage for query-related code.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 09:01:05 -04:00
Charis 4c8ed105d2 feat(studio): logs SQL execution wiring + source-aware run gestures (#48414)
## 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?

Feature (SQL editor: execution wiring for logs-source snippets). Part of
the stacked SQL-editor "Database vs Logs" query-source series.

## What is the current behavior?

The SQL editor only ever runs queries against the user's Postgres
database. There is no execution path for a logs (`log_sql`) snippet, and
the run-button telemetry event carries no backend discriminator.

## What is the new behavior?

- `useRunSource(id)` derives the run backend from the snippet type; a
`log_sql` snippet resolves to `{ type: 'logs', dateRange }`, pairing the
run with its session time range (default: last hour).
- `useLogsSqlExecution` runs a promoted `SafeLogSqlFragment` against the
analytics OTEL (ClickHouse) endpoint with the resolved time range as
`iso_timestamp_start`/`iso_timestamp_end` request params. The endpoint
is **pinned to OTEL** — a snippet's dialect must not flip with org
migration.
- The run gestures (toolbar button and Cmd+Enter) branch on the source
and promote with the matching `acceptUntrusted*` right at the user
action, preserving the auditable promotion-at-gesture boundary. pg
intellisense is gated off for logs snippets.
- The `sql_editor_query_run_button_clicked` telemetry event gains a
required `{ source: 'database' | 'logs' }` property, fired from both
execution paths.
- Capability guard: a `log_sql` snippet is reachable by direct URL
regardless of the (later) entry-point flag gating, so `executeLogsQuery`
short-circuits when `otelLegacyLogs` is off — recording a clear "not
available yet" result message instead of firing a request that would
only return an opaque backend error on a non-ClickHouse project. This is
a guard on the gesture, not endpoint selection.
- Tests: `useRunSource` routing, `useLogsSqlExecution`
endpoint/range/structured-error/capability-guard, and a reusable `flags`
option on `renderSqlEditorHook`.

No UI entry points are added — the feature runs dark until the
flag-gated creation/nav PRs later in the stack.

## Additional context

Stacked on the query-source series; base branch is `master` now that PR
4 (log date range domain + session state, #48401) is merged. Follow-ups
in the stack add the toolbar/creation UI (with a run-affordance gate on
`otelLegacyLogs`), nav section, AI dialect support, and reports guard.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added support for running log queries directly from the SQL editor.
* Log query results, errors, and time ranges are now handled within the
editor session.
* Added automatic selection between database and log query execution,
including support for custom date ranges.
* SQL assistance is disabled while editing log queries where database
definitions do not apply.

* **Tests**
* Added coverage for log query execution, date ranges, feature
availability, and execution source selection.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 10:43:48 -04:00
Charis fa5eb17277 feat(studio): discriminated snippet union + source-aware writes (#48313)
Stacked on #48305.

## What

PR 3 of the stacked SQL-editor query-source series (Database vs Logs).
Stacked on the PR 2 branch `charislam/log-sql-content-shape`.

Turns `SnippetWithContent` into a discriminated union on `type` and
makes all snippet writes source-aware:

- `data/content/sql-folders-query.ts`: `SnippetWithContent` is now `{
type: 'sql'; content?: SqlSnippets.Content } | { type: 'log_sql';
content?: LogSqlSnippets.Content } | { type: 'report'; content?: never
}`. `report` is kept (the content endpoints' wire type carries it) but
has no SQL content — its body is `Dashboards.Content`, loaded through
the separate `Content` union.
- `setSql` brands per type (`untrustedLogSql` vs `untrustedSql`).
- `buildUpsertPayload` persists `snippet.type` (no longer hardcoded
`'sql'`).
- `createSqlSnippetSkeletonV2({ source })` emits the matching type +
content shape with the `as any` cast removed.
- New `components/interfaces/SQLEditor/querySource.ts`:
`SqlSnippetSource` + `getSnippetSource`.
- `seedSnippet` test helper gains a `source` arg.
- New `remapWireSnippet` boundary helper in `content-remap.ts`
concentrates the single wire->domain assertion, so `content-id-query` /
`content-upsert-mutation` call sites are cast-free (no `as unknown as`).
- Collateral: query result types aligned to the union; `updateSnippet`
no longer accepts `type` (source is immutable); db-only editor read
paths narrow away `log_sql`.

## Why

Impossible-states-impossible typing: a snippet's brand follows its
content type, so logs SQL and database SQL can never cross execution
paths. No behavior change for existing database snippets.

## Testing

- \`pnpm typecheck\` — clean
- \`pnpm --filter studio run lint:ratchet\` — no new warnings
- \`pnpm test:studio\` (data/content, SQLEditor, state/sql-editor) —
passing, including new tests for \`getSnippetSource\`, source-aware
\`setSql\`, type-aware \`buildUpsertPayload\`, and both skeleton shapes.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added source-aware creation for SQL editor snippets, including
log-based SQL snippets.
* Introduced backend source mapping so log snippets are treated as
log_sql.
* **Bug Fixes**
* Improved SQL retrieval/prettification so log snippets no longer use
the wrong fallback content.
* Ensured log snippets are sanitized and preserve correct type, content,
identifiers, and statuses during save/upsert flows.
* **Tests**
* Expanded unit and integration coverage for log snippet creation,
source mapping, editing, prettification, and upsert payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 12:28:36 -04:00
Charis 3d83e026f9 refactor(sql-editor): finish EditorController/DiffController port (Step 2) (#48166)
## Summary
Step 2 of the SQL Editor testability plan.

`SQLEditorContext` already wrapped the Monaco refs and exposed a few
semantic imperative helpers (`getEditorSql`, `clearHighlights`,
`applyErrorHighlight`, `refocusEditor`, …). This finishes that
abstraction so no hook or controller touches
`editorRef.current`/`diffEditorRef.current` directly anymore — they only
call the port. The port is what will let Step 3's test harness inject a
real in-memory editor adapter instead of mocking Monaco; production
wires it to the real Monaco refs, unchanged.

- Extends the context value with two semantic controllers, backed by the
existing refs:
- `editor: EditorController` — `isReady`, `getValue`,
`getSelectionStartLine`, `getSql` (today's `getEditorSql`), `replaceAll`
(wraps the repeated `executeEdits(...)` pattern), `focus`,
`revealLineInCenter`, `highlightErrorLine` (today's
`applyErrorHighlight`), `clearHighlights`.
- `diff: DiffController` — `isMounted`, `getModifiedValue`, `setDiff`
(the diff-sync effect body), `attach` (today's `handleDiffEditorMount`).
- Migrates every touch point off raw refs onto the port:
`useSqlEditorExecution`, `usePrettifyQuery`, `useSqlEditorShortcuts`,
`SQLEditorControllers`' `readEditorSql`, and `useSqlEditorAi`'s
`acceptAiHandler`/`drainDiffRequest`/`handleDiffEditorMount`/diff-sync
effect.
- `SQLEditorEditorPanel.tsx` is intentionally left untouched — it wires
the raw refs into the real Monaco/DiffEditor React components for
rendering, which isn't decision logic to abstract.

Behavior-preserving.

## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor` (265 tests passing)
- [x] `pnpm --filter studio run lint:ratchet`
2026-07-21 16:22:47 -04:00
Ali Waseem b100272376 chore(sql-editor): remove Pretty Explain feature (#47981)
Removes the SQL Editor Pretty Explain feature — the Explain tab, the Run
EXPLAIN ANALYZE action + shortcut, and its dead plumbing. It's been
gated off behind the `DisablePrettyExplainOnSqlEditor` kill switch for
weeks with no usage or complaints.

`ExplainVisualizer` / `isExplainQuery` are kept — they're used
independently by Query Insights, Query Performance, and the EditorPanel
quick-runner. Manually-run `EXPLAIN` queries still render as raw rows in
the Results tab.

Typecheck, lint, and all affected unit tests pass.

Closes FE-3930

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Changes**
* Removed the SQL editor’s EXPLAIN execution workflow, including its
toolbar action, keyboard shortcut, utility tab, and visual query-plan
display.
  * Simplified query execution to focus on standard results and charts.
* Improved result clearing when switching databases and refined
execution error handling.
* Updated SQL editor state and tests to reflect the streamlined
experience.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-16 11:04:34 +08:00
Charis 9e80a159b5 refactor(sql-editor): decompose into controller contexts + presentational components (decompose 6/6) (#47938)
## What

Final step (**6 of 6**) of the `SQLEditor.tsx` decomposition. Splits the
two `ResizablePanel` bodies out of the `SQLEditorContent` monolith into
presentational sub-components:

- **`SQLEditorPane`** — the editor panel: loading state, `DiffEditor` +
diff ask-AI widget, `MonacoEditor` + ask-AI widget. Reads the Monaco
refs (`editorRef`/`monacoRef`/`diffEditorRef`) from `SQLEditorContext`,
and receives the `diff`/`prompt`/`ai` controllers plus reactive state as
props.
- **`SQLEditorResults`** — the results panel: loading state +
`UtilityPanel`.

`SQLEditorContent` is now a composition root: shared-ref provider, hook
composition, and the run-query warning modal.

## Result

`SQLEditor.tsx` goes from the original **1056-line** monolith down to
**259 lines** (≈75% reduction). The remaining size over a bare ~140-line
root is the run-query warning modal, kept inline **deliberately**: its
handlers hold the `acceptUntrustedSql` promotion, which must stay at the
explicit user-action boundary in the root rather than moving into a
presentational pane.

## Behavior-preserving

The moved JSX is byte-identical aside from prop threading. No logic,
effects, dependency arrays, or `eslint-disable`s changed. The two
**render-time ref reads** — the editor placeholder (`!promptState.isOpen
&& !editorRef.current?.getValue()`) and the ask-AI widget gate
(`editorRef.current && promptState.isOpen && !isDiffOpen`) — are
preserved verbatim in `SQLEditorPane`, which re-renders whenever the
`prompt`/`diff` props change, keeping those reads fresh (the guardrail
from the plan).

Verification (all green):
- `SQLEditor.test.tsx` characterization suite — 11/11 pass
- `tsc --noEmit` — no new errors
- eslint — clean (no new ratchet entries)
- prettier — clean

## Stack

Builds on decompose 5 (#47935). This is the last PR in the series — the
decomposition is complete after this merges.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a resizable SQL editor layout with run warnings, query results,
and an Explain view.
* Introduced centralized SQL editor controllers and expanded AI-assisted
prompt/diff workflows.
  * Added keyboard support for running and Explain analysis.
* **Bug Fixes**
  * Restored editor focus after accepting or discarding AI changes.
  * Improved handling and validation of SQL Explain actions.
  * Preserved editor scroll position when switching snippets.
* **Refactor**
* Streamlined the SQLEditor into a composed layout and improved
memoization to reduce unnecessary re-renders.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:10:09 -04:00