mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
studio: add SafeSql foundation utilities (1/7) (#45897)
## Summary First in a stack of seven PRs migrating the remaining `executeSql` call sites to `SafeSqlFragment`. This PR lands pure additions — shared types and helpers that later PRs import. - `apps/studio/lib/postgres-types.ts` — `SafePostgresColumn` / `SafePostgresTable` wrappers over `PGColumn` / `PGTable` that brand `check` as `SafeSqlFragment` and add the optional `format_schema` field - `apps/studio/lib/sql.ts` — `trimSafeSqlFragment` overload set that preserves the brand through `.trim()` - `apps/studio/lib/type-helpers.ts` — refine `DeepReadonly` so primitives pass through unchanged - `apps/studio/components/ui/SafeSqlInput.tsx` — `<Input>` wrapper that emits `SafeSqlFragment` via `rawSql` No consumers in this PR. The next PRs in the stack pick these up. ## Stack This is PR 1 of 7. The full sequence: 1. **This PR** — Foundation utilities 2. `charis/safe-sql-last/2` — pg-meta columns + ColumnTypeRef cascade 3. `charis/safe-sql-last/3` — pg-meta non-column SafeSql (functions/policies/triggers) 4. `charis/safe-sql-last/4` — Studio reports / query performance / privileges 5. `charis/safe-sql-last/6` — Stragglers + remaining tests 6. `charis/safe-sql-last/7` — Flip `executeSql` signature from `string` to `SafeSqlFragment` ## Test plan - [x] `pnpm typecheck` passes for the Studio target on this branch - [x] No runtime behavior changes — only type-level additions and a new unused component <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Introduced SafeSqlInput component providing secure and validated SQL query input handling with built-in constraints and error prevention. * **Refactor** * Enhanced internal type definitions and utilities for Postgres metadata handling and SQL operations to improve code reliability and maintainability. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45897) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
0dd23ba5a6
commit
2d2cc95caa
4 files changed
+49
-5
No files matched your search
@@ -0,0 +1,15 @@
|
||||
import { rawSql, type SafeSqlFragment } from '@supabase/pg-meta'
|
||||
import type { ChangeEvent, ComponentProps } from 'react'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
|
||||
type InputProps = ComponentProps<typeof Input>
|
||||
|
||||
export type SafeSqlInputProps = Omit<InputProps, 'placeholder' | 'value' | 'onChange'> & {
|
||||
placeholder?: SafeSqlFragment
|
||||
value: SafeSqlFragment
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>, value: SafeSqlFragment) => void
|
||||
}
|
||||
|
||||
export const SafeSqlInput = ({ onChange, ...props }: SafeSqlInputProps) => (
|
||||
<Input {...props} onChange={(event) => onChange?.(event, rawSql(event.target.value))} />
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { PGColumn, PGTable, SafeSqlFragment } from '@supabase/pg-meta'
|
||||
|
||||
export type SafePostgresColumn = PGColumn & {
|
||||
check: SafeSqlFragment | null
|
||||
// Present when sourced from pg-meta's SQL queries (e.g. `pgMeta.tables.retrieve`), absent
|
||||
// from the legacy `/platform/pg-meta/{ref}/tables` REST endpoint.
|
||||
format_schema?: string
|
||||
}
|
||||
|
||||
export type SafePostgresTable = Omit<PGTable, 'columns'> & {
|
||||
columns?: SafePostgresColumn[] | undefined
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { SafeSqlFragment } from '@supabase/pg-meta'
|
||||
|
||||
export function trimSafeSqlFragment(fragment: SafeSqlFragment): SafeSqlFragment
|
||||
export function trimSafeSqlFragment(fragment: SafeSqlFragment | null): SafeSqlFragment | null
|
||||
export function trimSafeSqlFragment(
|
||||
fragment: SafeSqlFragment | undefined
|
||||
): SafeSqlFragment | undefined
|
||||
export function trimSafeSqlFragment(
|
||||
fragment: SafeSqlFragment | null | undefined
|
||||
): SafeSqlFragment | null | undefined {
|
||||
if (fragment == null) return fragment
|
||||
return fragment.trim() as SafeSqlFragment
|
||||
}
|
||||
@@ -2,8 +2,12 @@ export type PlainObject<Value = unknown> = Record<string | number | symbol, Valu
|
||||
|
||||
export type Prettify<T> = { [K in keyof T]: T[K] } & {}
|
||||
|
||||
export type DeepReadonly<T> = T extends (infer R)[]
|
||||
? ReadonlyArray<DeepReadonly<R>>
|
||||
: T extends Object
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T
|
||||
type Primitive = string | number | bigint | boolean | symbol | null | undefined
|
||||
|
||||
export type DeepReadonly<T> = T extends Primitive
|
||||
? T
|
||||
: T extends (infer R)[]
|
||||
? ReadonlyArray<DeepReadonly<R>>
|
||||
: T extends object
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T
|
||||
Reference in new issue
Block a user