Files
supabase/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.utils.ts
Joshen Lim 4dc973048d Add confirmation modal when running notebook if notebook contains query cells that aren't read only (#49376)
## Context

Adds a confirmation modal when hitting "run notebook" if the notebook
contains any query cells that involve any sort of mutation (insert,
update, alter, etc, etc). Also gives users the option to run the
notebook's read only cells as an alternative.

<img width="432" height="355" alt="image"
src="https://github.com/user-attachments/assets/0413a3ad-5419-4c83-8bf3-976bfa683b9a"
/>


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

* **New Features**
* Added confirmation prompts before running queries that may modify data
or database structure.
* Prompts identify potentially mutating notebook queries and allow
running read-only cells instead.
* Query execution now includes checks for destructive operations and
missing row-level security, with optional automatic setup.
* Notebook runs use the latest saved and unsaved SQL and reliably reset
execution status.

* **Bug Fixes**
* Improved notebook layout behavior so content shrinks correctly within
flexible sections.

* **Tests**
* Expanded coverage for mutation detection, comments, multiple
statements, live SQL, and cell filtering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-24 14:15:56 +08:00

42 lines
1.6 KiB
TypeScript

import { type Snapshot } from 'valtio'
import { type Cell, type DatabaseCell } from '@/data/content/notebooks/notebook-schema'
import { removeCommentsFromSql } from '@/lib/helpers'
const MUTATING_STATEMENT_REGEX =
/^\s*(insert|update|delete|create|alter|drop|truncate|grant|revoke|merge)\b/i
/**
* Whether `sql` contains any statement that writes to data or schema, as opposed to a
* read-only query. Checked per `;`-delimited statement so a mutating statement anywhere
* in a multi-statement cell is caught, not just when it leads.
*/
export function isMutatingSql(sql: string): boolean {
const cleanedSql = removeCommentsFromSql(sql)
return cleanedSql.split(';').some((statement) => MUTATING_STATEMENT_REGEX.test(statement))
}
/**
* The database cells whose SQL mutates data or schema, for flagging before a notebook-wide run.
*/
export function findMutatingQueryCells({
cells,
getLiveSql,
}: {
cells: readonly Snapshot<Cell>[]
/**
* Lets a caller also check each cell's live editor buffer as well. The store
* only updates on a Monaco blur commit, which fires asynchronously, so a scan
* against the store alone can miss SQL typed just before the run click.
* */
getLiveSql?: (cellId: string) => string | undefined
}): { id: string; title: string }[] {
return cells
.filter((cell): cell is Snapshot<DatabaseCell> => cell._tag === 'database_cell')
.filter((cell) => {
const liveSql = getLiveSql?.(cell._id)
return isMutatingSql(cell.unchecked_sql) || (liveSql !== undefined && isMutatingSql(liveSql))
})
.map((cell) => ({ id: cell._id, title: cell.title ?? 'Untitled query' }))
}