## 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`
## Summary
Part 2/6 of the SQL Editor testability follow-up, stacked on #47980 (the
analyzeQueryIssues/resolveConnectionString PR).
- Extracts `shouldAutoGenerateTitle` and `buildExecuteParams` out of
`useSqlEditorExecution`'s inline logic into `SQLEditor.utils.ts`.
- Merges `checkIfAppendLimitRequired` and `suffixWithLimit` into a
single `applyAutoLimit` function — the two were only ever called
together and re-parsed the same query twice at every call site.
`applyAutoLimit` only accepts `SafeSqlFragment` (never a plain string)
and composes the `LIMIT` suffix through `safeSql`/`literal` rather than
raw template concatenation, so the only place in the file that reasserts
the `SafeSqlFragment` brand on a derived string is the small, dedicated
`trimTrailingSemicolons` helper — removing existing terminators can't
introduce unsafe content, unlike gluing new text onto the fragment.
- Updates the two other `checkIfAppendLimitRequired`/`suffixWithLimit`
call sites (`EditorPanel.tsx`, `ReportBlock.tsx`) accordingly;
`ReportBlock` now promotes its report SQL once and reuses the result for
both its display-only auto-limit hint and its execution, instead of
promoting twice.
## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor ReportBlock EditorPanel` (239 tests
passing)
## Summary
Part 1/6 of the SQL Editor testability follow-up (extracting pure
decision logic out of the SQL editor hooks so it can be exhaustively
unit-tested without mocking).
- Extracts `analyzeQueryIssues` and `hasBlockingIssues` out of
`useSqlEditorExecution`'s inline destructive-query /
warning-modal-gating logic into `SQLEditor.utils.ts`.
- Extracts `resolveConnectionString`, deduping the
`databases?.find(...)` lookup that was duplicated verbatim in both
`useSqlEditorExecution` and `useSqlEditorExplain`.
- Behavior-preserving — same runtime logic, now unit-testable in
isolation.
## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor.utils` (159 tests passing, includes
new exhaustive-permutation cases for the three extracted functions)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved SQL safety checks before execution, including destructive
queries, unsafe updates, database-altering commands, and tables missing
row-level security.
* Correctly recognizes tables protected by active security triggers.
* Improved database connection selection for primary and read-replica
databases.
* Preserved the ability to run queries with force enabled when safety
warnings are present.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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 -->
## 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 -->
## Summary
PR **4 of 6** in the SQLEditor decomposition stack. Extracts the two
query-run concerns out of the composition root into cohesive hooks.
Behavior-preserving — the characterization suite stays green.
## New hooks
- `useSqlEditorExecution` — the execute mutation, the `executeQuery`
pipeline (destructive-query gating, lazy title generation, connection
resolution), and the `potentialIssues` warning-modal state.
- `useSqlEditorExplain` — the execute-explain mutation and the
`executeExplainQuery` pipeline.
Both pipelines accept an **already-promoted `SafeSqlFragment`**.
## Safe-SQL boundary
Per review guidance, `acceptUntrustedSql` promotion stays in the
**composition root's** run/explain gesture handlers and warning-modal
confirm handlers — as close to the explicit user action as possible, so
it's auditable. The hooks never call `acceptUntrustedSql`; they only
accept safe SQL. `executeQuery` lists its (now all-stable) dependencies
directly rather than suppressing `react-hooks/exhaustive-deps`.
## Verification
- `vitest` — 11 characterization tests pass
- `pnpm --filter studio typecheck` — clean for SQL editor files
- `eslint` / `lint:ratchet` — pass (0 new violations)
- `prettier --check` — clean
`SQLEditorContent` is down to ~530 lines (from 1056 at the start of the
stack). Remaining: PR5 (AI/diff + shortcuts), PR6 (JSX pane split +
cleanup).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved SQL query execution safeguards for potentially destructive
statements, unsafe updates, and missing row-level security coverage.
* Improved EXPLAIN handling, including clearer validation for
unsupported multi-statement queries.
* Preserved query results, errors, and EXPLAIN output more reliably in
the SQL editor.
* **Improvements**
* Added clearer execution status handling and more consistent editor
focus, highlighting, and utility-tab behavior.
* Improved connection selection and SQL execution reliability across
supported database configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->