Files
supabase/apps/studio/components/interfaces/Integrations/Queues/Queues.utils.tsx
Charis 116faefcda studio: convert more executeSql callers to SafeSqlFragment (#45645)
## Summary

- Converts ~27 `executeSql` call sites in `apps/studio/data/**` to build
SQL through `safeSql` / `ident` / `literal` / `keyword` /
`joinSqlFragments` instead of raw template-string interpolation.
- Tightens the `useDatabaseCronJobCreateMutation` and
`useDatabaseEventTriggerCreateMutation` `sql`/`query` parameter types
from `string` to `SafeSqlFragment` (callers already produce one).
- Updates `getDeleteEnumeratedTypeSQL` in `packages/pg-meta` to return
`SafeSqlFragment`.
- Fixes a bug noticed while testing where Queues integration does not
correctly handle queues with uppercase names.

## Pages to manually test

- Integrations > Cron Jobs
- Integrations > Queues
- Database > Triggers > Event Triggers
- Database > Indexes
- Reports > Query Performance
- Storage

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

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
  * Queue lookups now correctly handle case-insensitive queue names.
* Queue table references are now properly managed and consistently
applied throughout the queue management interface.
  * Improved queue name display normalization in the user interface.

* **Chores**
* Enhanced SQL query safety across the database layer through
parameterized query construction and safer templating approaches.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-06 12:21:48 -04:00

136 lines
4.0 KiB
TypeScript

import { Column } from 'react-data-grid'
import { cn } from 'ui'
import z from 'zod'
import {
QueueCreatedAtCell,
QueueNameCell,
QueueRLSCell,
QueueSizeCell,
QueueTypeCell,
QueueWithMetrics,
} from './QueueCells'
import { PostgresQueue } from '@/data/database-queues/database-queues-query'
export const formatQueueColumns = (): Column<QueueWithMetrics>[] => {
return [
{
key: 'queue_name',
name: 'Name',
resizable: true,
minWidth: 200,
headerCellClass: undefined,
renderHeaderCell: () => {
return (
<div className={cn('flex items-center justify-between font-normal text-xs w-full ml-8')}>
<p className="text-foreground!">Name</p>
</div>
)
},
renderCell: (props) => {
return <QueueNameCell queue={props.row} />
},
},
{
key: 'type',
name: 'Type',
resizable: true,
minWidth: 120,
headerCellClass: undefined,
renderHeaderCell: () => {
return (
<div className={cn('flex items-center justify-between font-normal text-xs w-full')}>
<p className="text-foreground!">Type</p>
</div>
)
},
renderCell: (props) => {
return <QueueTypeCell queue={props.row} />
},
},
{
key: 'rls_enabled',
name: 'RLS enabled',
resizable: true,
minWidth: 120,
headerCellClass: undefined,
renderHeaderCell: () => {
return (
<div className={cn('flex items-center justify-between font-normal text-xs w-full')}>
<p className="text-foreground!">RLS enabled</p>
</div>
)
},
renderCell: (props) => {
return <QueueRLSCell queue={props.row} />
},
},
{
key: 'created_at',
name: 'Created at',
resizable: true,
minWidth: 180,
headerCellClass: undefined,
renderHeaderCell: () => {
return (
<div className={cn('flex items-center justify-between font-normal text-xs w-full')}>
<p className="text-foreground!">Created at</p>
</div>
)
},
renderCell: (props) => {
return <QueueCreatedAtCell queue={props.row} />
},
},
{
key: 'queue_size',
name: 'Size',
resizable: true,
minWidth: 120,
headerCellClass: undefined,
renderHeaderCell: () => {
return (
<div className={cn('flex items-center justify-between font-normal text-xs w-full')}>
<p className="text-foreground!">Size</p>
</div>
)
},
renderCell: (props) => {
return <QueueSizeCell queue={props.row} />
},
},
]
}
export const prepareQueuesForDataGrid = (queues: PostgresQueue[]): QueueWithMetrics[] => {
return queues.map((queue) => ({
...queue,
id: queue.queue_name, // Use queue_name as unique id
}))
}
// pgmq stores queue names as-provided in pgmq.meta and lowercases them only when
// building the underlying q_/a_ relations, so uppercase queue names are fine —
// the user-facing name is preserved and table-name lookups go through
// pgmqQueueTable / pgmqArchiveTable which lowercase to match pgmq's behavior.
export const QueueNameSchema = z
.string()
.trim()
.min(1, 'Please provide a name for your queue')
.max(47, "The name can't be longer than 47 characters")
.regex(
/^[a-zA-Z0-9_-]+$/,
'Name must contain only alphanumeric characters, underscores, and hyphens'
)
export const isQueueNameValid = (queueName: string) => {
return QueueNameSchema.safeParse(queueName).success
}
// pgmq.format_table_name() lowercases its input, so the actual relations in the
// pgmq schema are always q_/a_ followed by the lowercased queue name — even when
// pgmq.meta stores the original casing. Use these helpers anywhere a queue name
// is mapped to a relname.
export const pgmqQueueTable = (queueName: string) => `q_${queueName.toLowerCase()}`
export const pgmqArchiveTable = (queueName: string) => `a_${queueName.toLowerCase()}`