From 116faefcdad1b8de611a5ce90ce1dcb6d9abcbea Mon Sep 17 00:00:00 2001
From: Charis <26616127+charislam@users.noreply.github.com>
Date: Wed, 6 May 2026 12:21:48 -0400
Subject: [PATCH] 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
## 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.
---
.../CreateQueueSheet.schema.ts | 4 +-
.../Integrations/Queues/QueueCells.tsx | 3 +-
.../Integrations/Queues/QueuePage.tsx | 5 +-
.../Integrations/Queues/QueueTab.tsx | 6 ++-
.../Integrations/Queues/Queues.utils.tsx | 18 +++++--
.../Integrations/Queues/QueuesRows.tsx | 3 +-
.../Integrations/Queues/QueuesSettings.tsx | 21 ++++++--
.../Queues/SingleQueue/QueueSettings.tsx | 9 ++--
.../data/config/disk-breakdown-query.ts | 3 +-
.../database-cron-job-query.ts | 5 +-
.../database-cron-job-run-mutation.ts | 9 ++--
.../database-cron-jobs-count-query.ts | 3 +-
.../database-cron-jobs-create-mutation.ts | 3 +-
.../database-cron-jobs-delete-mutation.ts | 3 +-
.../database-cron-jobs-runs-infinite-query.ts | 11 ++--
.../database-cron-jobs-toggle-mutation.ts | 3 +-
.../database-cron-timezone-query.ts | 3 +-
.../database-event-trigger-create-mutation.ts | 3 +-
.../database-event-triggers-query.ts | 3 +-
.../database-indexes/index-create-mutation.ts | 12 ++---
.../database-indexes/index-delete-mutation.ts | 4 +-
.../stripe/sync-state-query.ts | 3 +-
...atabase-queue-messages-archive-mutation.ts | 4 +-
...database-queue-messages-delete-mutation.ts | 4 +-
.../database-queue-messages-infinite-query.ts | 50 ++++++++++++-------
.../database-queue-messages-read-mutation.ts | 4 +-
.../database-queue-messages-send-mutation.ts | 4 +-
.../database-queues-create-mutation.ts | 21 +++++---
.../database-queues-delete-mutation.ts | 4 +-
.../database-queues-metrics-query.ts | 21 ++++----
.../database-queues-purge-mutation.ts | 4 +-
.../retrieve-index-from-select-query.ts | 7 +--
.../database/supamonitor-enabled-query.ts | 3 +-
...blic-buckets-with-select-policies-query.ts | 4 +-
apps/studio/data/tables/table-names-query.ts | 5 +-
.../pg-meta/src/sql/studio/database/types.ts | 2 +-
36 files changed, 173 insertions(+), 101 deletions(-)
diff --git a/apps/studio/components/interfaces/Integrations/Queues/CreateQueueSheet/CreateQueueSheet.schema.ts b/apps/studio/components/interfaces/Integrations/Queues/CreateQueueSheet/CreateQueueSheet.schema.ts
index e4ff0c93fd0..1499776e72d 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/CreateQueueSheet/CreateQueueSheet.schema.ts
+++ b/apps/studio/components/interfaces/Integrations/Queues/CreateQueueSheet/CreateQueueSheet.schema.ts
@@ -1,6 +1,6 @@
import z from 'zod'
-import { QueryNameSchema } from '../Queues.utils'
+import { QueueNameSchema } from '../Queues.utils'
const normalQueueSchema = z.object({
type: z.literal('basic'),
@@ -17,7 +17,7 @@ const unloggedQueueSchema = z.object({
})
export const FormSchema = z.object({
- name: QueryNameSchema,
+ name: QueueNameSchema,
enableRls: z.boolean(),
values: z.discriminatedUnion('type', [
normalQueueSchema,
diff --git a/apps/studio/components/interfaces/Integrations/Queues/QueueCells.tsx b/apps/studio/components/interfaces/Integrations/Queues/QueueCells.tsx
index c72ea63e238..7deaf9eb4a3 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/QueueCells.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/QueueCells.tsx
@@ -1,6 +1,7 @@
import dayjs from 'dayjs'
import { Check, Loader2, X } from 'lucide-react'
+import { pgmqQueueTable } from './Queues.utils'
import { useQueuesMetricsQuery } from '@/data/database-queues/database-queues-metrics-query'
import { PostgresQueue } from '@/data/database-queues/database-queues-query'
import { useTablesQuery } from '@/data/tables/tables-query'
@@ -43,7 +44,7 @@ export const QueueRLSCell = ({ queue }: QueueCellProps) => {
schema: 'pgmq',
})
- const queueTable = queueTables?.find((x) => x.name === `q_${queue.queue_name}`)
+ const queueTable = queueTables?.find((x) => x.name === pgmqQueueTable(queue.queue_name))
const isRlsEnabled = !!queueTable?.rls_enabled
return (
diff --git a/apps/studio/components/interfaces/Integrations/Queues/QueuePage.tsx b/apps/studio/components/interfaces/Integrations/Queues/QueuePage.tsx
index a6b4c122472..aebef928b7b 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/QueuePage.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/QueuePage.tsx
@@ -34,7 +34,10 @@ export const QueuePage = () => {
connectionString: project?.connectionString,
})
- const currentQueue = queues?.find((queue) => queue.queue_name === childId)
+ // pgmq is case-insensitive when storing queue names — compare lowercased to be safe
+ const currentQueue = queues?.find(
+ (queue) => queue.queue_name.toLowerCase() === childId?.toLowerCase()
+ )
const pageTitle = childLabel || childId || 'Queue'
diff --git a/apps/studio/components/interfaces/Integrations/Queues/QueueTab.tsx b/apps/studio/components/interfaces/Integrations/Queues/QueueTab.tsx
index fd8178dd12b..37c29c8d191 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/QueueTab.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/QueueTab.tsx
@@ -16,6 +16,7 @@ import {
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
+import { pgmqQueueTable } from './Queues.utils'
import { DeleteQueue } from '@/components/interfaces/Integrations/Queues/SingleQueue/DeleteQueue'
import { PurgeQueue } from '@/components/interfaces/Integrations/Queues/SingleQueue/PurgeQueue'
import { QUEUE_MESSAGE_TYPE } from '@/components/interfaces/Integrations/Queues/SingleQueue/Queue.utils'
@@ -51,7 +52,8 @@ export const QueueTab = () => {
connectionString: project?.connectionString,
schema: 'pgmq',
})
- const queueTable = tables?.find((x) => x.name === `q_${queueName}`)
+ const queueRelname = queueName ? pgmqQueueTable(queueName) : undefined
+ const queueTable = tables?.find((x) => x.name === queueRelname)
const isRlsEnabled = queueTable?.rls_enabled ?? false
const { data: policies } = useDatabasePoliciesQuery({
@@ -59,7 +61,7 @@ export const QueueTab = () => {
connectionString: project?.connectionString,
schema: 'pgmq',
})
- const queuePolicies = (policies ?? []).filter((policy) => policy.table === `q_${queueName}`)
+ const queuePolicies = (policies ?? []).filter((policy) => policy.table === queueRelname)
const { data: isExposed } = useQueuesExposePostgrestStatusQuery({
projectRef: project?.ref,
diff --git a/apps/studio/components/interfaces/Integrations/Queues/Queues.utils.tsx b/apps/studio/components/interfaces/Integrations/Queues/Queues.utils.tsx
index a3fbea3a28a..8540a7398d2 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/Queues.utils.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/Queues.utils.tsx
@@ -109,7 +109,11 @@ export const prepareQueuesForDataGrid = (queues: PostgresQueue[]): QueueWithMetr
}))
}
-export const QueryNameSchema = z
+// 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')
@@ -119,9 +123,13 @@ export const QueryNameSchema = z
'Name must contain only alphanumeric characters, underscores, and hyphens'
)
-/**
- * Checks if the queue name is valid. Returns a boolean.
- */
export const isQueueNameValid = (queueName: string) => {
- return QueryNameSchema.safeParse(queueName).success
+ 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()}`
diff --git a/apps/studio/components/interfaces/Integrations/Queues/QueuesRows.tsx b/apps/studio/components/interfaces/Integrations/Queues/QueuesRows.tsx
index edc6916bf09..b5aa5e781e4 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/QueuesRows.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/QueuesRows.tsx
@@ -3,6 +3,7 @@ import { includes, sortBy } from 'lodash'
import { Check, ChevronRight, Loader2, X } from 'lucide-react'
import { useRouter } from 'next/router'
+import { pgmqQueueTable } from './Queues.utils'
import Table from '@/components/to-be-cleaned/Table'
import { useQueuesMetricsQuery } from '@/data/database-queues/database-queues-metrics-query'
import { PostgresQueue } from '@/data/database-queues/database-queues-query'
@@ -24,7 +25,7 @@ const QueueRow = ({ queue }: { queue: PostgresQueue }) => {
connectionString: selectedProject?.connectionString,
schema: 'pgmq',
})
- const queueTable = queueTables?.find((x) => x.name === `q_${queue.queue_name}`)
+ const queueTable = queueTables?.find((x) => x.name === pgmqQueueTable(queue.queue_name))
const isRlsEnabled = !!queueTable?.rls_enabled
const { data: metrics, isPending: isLoading } = useQueuesMetricsQuery(
diff --git a/apps/studio/components/interfaces/Integrations/Queues/QueuesSettings.tsx b/apps/studio/components/interfaces/Integrations/Queues/QueuesSettings.tsx
index c226d6a2ead..ef25daa356d 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/QueuesSettings.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/QueuesSettings.tsx
@@ -21,6 +21,7 @@ import { InlineLink } from '@/components/ui/InlineLink'
import { useProjectPostgrestConfigQuery } from '@/data/config/project-postgrest-config-query'
import { useProjectPostgrestConfigUpdateMutation } from '@/data/config/project-postgrest-config-update-mutation'
import { useQueuesExposePostgrestStatusQuery } from '@/data/database-queues/database-queues-expose-postgrest-status-query'
+import { useQueuesQuery } from '@/data/database-queues/database-queues-query'
import { useDatabaseQueueToggleExposeMutation } from '@/data/database-queues/database-queues-toggle-postgrest-mutation'
import { useDatabaseQueuesVersionQuery } from '@/data/database-queues/database-queues-version-query'
import { useTableUpdateMutation } from '@/data/tables/table-update-mutation'
@@ -56,6 +57,18 @@ export const QueuesSettings = () => {
const tablesWithoutRLS =
queueTables?.filter((x) => x.name.startsWith('q_') && !x.rls_enabled) ?? []
+ // pgmq lowercases queue names when building q_/a_ relations, but pgmq.meta keeps
+ // the original casing. Look up each relname in list_queues() so we render the
+ // user-provided name rather than the lowercased relname slice.
+ const { data: queues } = useQueuesQuery({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ })
+ const queueDisplayName = (relname: string) => {
+ const stripped = relname.slice(2)
+ return queues?.find((q) => q.queue_name.toLowerCase() === stripped)?.queue_name ?? stripped
+ }
+
const { data: config, error: configError } = useProjectPostgrestConfigQuery({
projectRef: project?.ref,
})
@@ -259,7 +272,9 @@ export const QueuesSettings = () => {
{tablesWithoutRLS.map((x) => {
return (
- {x.name.slice(2)}
+
+ {queueDisplayName(x.name)}
+
)
})}
@@ -272,7 +287,7 @@ export const QueuesSettings = () => {
>
Enable RLS on{' '}
{tablesWithoutRLS.length === 1
- ? tablesWithoutRLS[0].name.slice(2)
+ ? queueDisplayName(tablesWithoutRLS[0].name)
: `${tablesWithoutRLS.length} queues`}
@@ -354,7 +369,7 @@ export const QueuesSettings = () => {
{tablesWithoutRLS.map((x) => {
return (
- {x.name.slice(2)}
+ {queueDisplayName(x.name)}
)
})}
diff --git a/apps/studio/components/interfaces/Integrations/Queues/SingleQueue/QueueSettings.tsx b/apps/studio/components/interfaces/Integrations/Queues/SingleQueue/QueueSettings.tsx
index af634743e83..451ec22cd4b 100644
--- a/apps/studio/components/interfaces/Integrations/Queues/SingleQueue/QueueSettings.tsx
+++ b/apps/studio/components/interfaces/Integrations/Queues/SingleQueue/QueueSettings.tsx
@@ -28,6 +28,7 @@ import {
import { Admonition } from 'ui-patterns/admonition'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
+import { pgmqArchiveTable, pgmqQueueTable } from '../Queues.utils'
import { getQueueFunctionsMapping } from './Queue.utils'
import AlertError from '@/components/ui/AlertError'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
@@ -83,15 +84,17 @@ export const QueueSettings = ({}: QueueSettingsProps) => {
connectionString: project?.connectionString,
schema: 'pgmq',
})
- const queueTable = queueTables?.find((x) => x.name === `q_${name}`)
- const archiveTable = queueTables?.find((x) => x.name === `a_${name}`)
+ const queueRelname = name ? pgmqQueueTable(name) : undefined
+ const archiveRelname = name ? pgmqArchiveTable(name) : undefined
+ const queueTable = queueTables?.find((x) => x.name === queueRelname)
+ const archiveTable = queueTables?.find((x) => x.name === archiveRelname)
const { data: allTablePrivileges, isSuccess: isSuccessPrivileges } = useTablePrivilegesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const queuePrivileges = allTablePrivileges?.find(
- (x) => x.schema === 'pgmq' && x.name === `q_${name}`
+ (x) => x.schema === 'pgmq' && x.name === queueRelname
)
const { mutateAsync: grantPrivilege } = useTablePrivilegesGrantMutation()
diff --git a/apps/studio/data/config/disk-breakdown-query.ts b/apps/studio/data/config/disk-breakdown-query.ts
index ebfbc28521f..df51ee0f0b0 100644
--- a/apps/studio/data/config/disk-breakdown-query.ts
+++ b/apps/studio/data/config/disk-breakdown-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { configKeys } from './keys'
@@ -25,7 +26,7 @@ export async function getDiskBreakdown(
{
projectRef,
connectionString,
- sql: `
+ sql: safeSql`
SELECT
(
SELECT
diff --git a/apps/studio/data/database-cron-jobs/database-cron-job-query.ts b/apps/studio/data/database-cron-jobs/database-cron-job-query.ts
index e83bdf00109..368ec52407e 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-job-query.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-job-query.ts
@@ -1,3 +1,4 @@
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { CronJob } from './database-cron-jobs-infinite-query'
@@ -24,8 +25,8 @@ export async function getDatabaseCronJob({
projectRef,
connectionString,
sql: !!id
- ? `SELECT * FROM cron.job where jobid = ${id};`
- : `SELECT * FROM cron.job where jobname = '${name}';`,
+ ? safeSql`SELECT * FROM cron.job where jobid = ${literal(id)};`
+ : safeSql`SELECT * FROM cron.job where jobname = ${literal(name)};`,
queryKey: ['cron-job', id],
})
diff --git a/apps/studio/data/database-cron-jobs/database-cron-job-run-mutation.ts b/apps/studio/data/database-cron-jobs/database-cron-job-run-mutation.ts
index 3846630be03..a9200798503 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-job-run-mutation.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-job-run-mutation.ts
@@ -1,3 +1,4 @@
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -22,15 +23,13 @@ export async function runDatabaseCronJobCommand({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `
-DO $$
+ sql: safeSql`DO $$
DECLARE
job_command text;
BEGIN
- select command into job_command from cron.job where jobid = ${jobId};
+ select command into job_command from cron.job where jobid = ${literal(jobId)};
EXECUTE job_command;
-END $$;
-`.trim(),
+END $$;`,
queryKey: databaseCronJobsKeys.create(),
})
diff --git a/apps/studio/data/database-cron-jobs/database-cron-jobs-count-query.ts b/apps/studio/data/database-cron-jobs/database-cron-jobs-count-query.ts
index 49270d0cc4c..9fcb6d0bdca 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-jobs-count-query.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-jobs-count-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { databaseCronJobsKeys } from './keys'
@@ -9,7 +10,7 @@ type DatabaseCronJobsCountVariables = {
connectionString?: string | null
}
-const cronJobCountSql = `select count(jobid) from cron.job;`.trim()
+const cronJobCountSql = safeSql`select count(jobid) from cron.job;`
export async function getDatabaseCronJobsCount({
projectRef,
diff --git a/apps/studio/data/database-cron-jobs/database-cron-jobs-create-mutation.ts b/apps/studio/data/database-cron-jobs/database-cron-jobs-create-mutation.ts
index 22a5b49989c..941d4df8181 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-jobs-create-mutation.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-jobs-create-mutation.ts
@@ -1,3 +1,4 @@
+import type { SafeSqlFragment } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -8,7 +9,7 @@ import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type DatabaseCronJobCreateVariables = {
projectRef: string
connectionString?: string | null
- query: string
+ query: SafeSqlFragment
searchTerm?: string
identifier?: string | number
}
diff --git a/apps/studio/data/database-cron-jobs/database-cron-jobs-delete-mutation.ts b/apps/studio/data/database-cron-jobs/database-cron-jobs-delete-mutation.ts
index 986c84e2187..eda2c0674a6 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-jobs-delete-mutation.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-jobs-delete-mutation.ts
@@ -1,3 +1,4 @@
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -20,7 +21,7 @@ export async function deleteDatabaseCronJob({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `SELECT cron.unschedule(${jobId});`,
+ sql: safeSql`SELECT cron.unschedule(${literal(jobId)});`,
queryKey: databaseCronJobsKeys.delete(),
})
diff --git a/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts b/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts
index 82719814776..02338e053a4 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts
@@ -1,3 +1,4 @@
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { InfiniteData, useInfiniteQuery } from '@tanstack/react-query'
import { last } from 'lodash'
@@ -37,13 +38,15 @@ export async function getDatabaseCronJobRuns({
// Use runid for ordering and pagination since it's the primary key (indexed)
// and preserves chronological order (auto-incrementing)
- let query = `
+ const afterRunIdClause =
+ typeof afterRunId === 'number' ? safeSql`AND runid < ${literal(afterRunId)}` : safeSql``
+ const query = safeSql`
SELECT * FROM cron.job_run_details
WHERE
- jobid = '${jobId}'
- ${typeof afterRunId === 'number' ? `AND runid < '${afterRunId}'` : ''}
+ jobid = ${literal(jobId)}
+ ${afterRunIdClause}
ORDER BY runid DESC
- LIMIT ${CRON_JOB_RUNS_PAGE_SIZE}`
+ LIMIT ${literal(CRON_JOB_RUNS_PAGE_SIZE)}`
const { result } = await executeSql({
projectRef,
diff --git a/apps/studio/data/database-cron-jobs/database-cron-jobs-toggle-mutation.ts b/apps/studio/data/database-cron-jobs/database-cron-jobs-toggle-mutation.ts
index b091d159181..3e5bc7751b0 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-jobs-toggle-mutation.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-jobs-toggle-mutation.ts
@@ -1,3 +1,4 @@
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -22,7 +23,7 @@ export async function toggleDatabaseCronJob({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `select cron.alter_job(job_id := ${jobId}, active := ${active});`,
+ sql: safeSql`select cron.alter_job(job_id := ${literal(jobId)}, active := ${literal(active)});`,
queryKey: databaseCronJobsKeys.alter(),
})
diff --git a/apps/studio/data/database-cron-jobs/database-cron-timezone-query.ts b/apps/studio/data/database-cron-jobs/database-cron-timezone-query.ts
index a85a0e5521b..2d0f518780d 100644
--- a/apps/studio/data/database-cron-jobs/database-cron-timezone-query.ts
+++ b/apps/studio/data/database-cron-jobs/database-cron-timezone-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { databaseCronJobsKeys } from './keys'
@@ -18,7 +19,7 @@ export async function getDatabaseCronTimezone({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `select setting from pg_settings where name = 'cron.timezone';`,
+ sql: safeSql`select setting from pg_settings where name = 'cron.timezone';`,
})
return result[0].setting
}
diff --git a/apps/studio/data/database-event-triggers/database-event-trigger-create-mutation.ts b/apps/studio/data/database-event-triggers/database-event-trigger-create-mutation.ts
index b193ea1a593..2740821cb02 100644
--- a/apps/studio/data/database-event-triggers/database-event-trigger-create-mutation.ts
+++ b/apps/studio/data/database-event-triggers/database-event-trigger-create-mutation.ts
@@ -1,3 +1,4 @@
+import type { SafeSqlFragment } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -8,7 +9,7 @@ import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type DatabaseEventTriggerCreateVariables = {
projectRef: string
connectionString?: string | null
- sql: string
+ sql: SafeSqlFragment
}
export async function createDatabaseEventTrigger({
diff --git a/apps/studio/data/database-event-triggers/database-event-triggers-query.ts b/apps/studio/data/database-event-triggers/database-event-triggers-query.ts
index 9f34a1873ab..2660952375f 100644
--- a/apps/studio/data/database-event-triggers/database-event-triggers-query.ts
+++ b/apps/studio/data/database-event-triggers/database-event-triggers-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { databaseEventTriggerKeys } from './keys'
@@ -22,7 +23,7 @@ export type DatabaseEventTrigger = {
function_definition: string | null
}
-const EVENT_TRIGGERS_SQL = `
+const EVENT_TRIGGERS_SQL = safeSql`
select
evt.oid,
evt.evtname as name,
diff --git a/apps/studio/data/database-indexes/index-create-mutation.ts b/apps/studio/data/database-indexes/index-create-mutation.ts
index 199749b9d78..08888d88298 100644
--- a/apps/studio/data/database-indexes/index-create-mutation.ts
+++ b/apps/studio/data/database-indexes/index-create-mutation.ts
@@ -1,4 +1,4 @@
-import { ident } from '@supabase/pg-meta/src/pg-format'
+import { ident, joinSqlFragments, keyword, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -24,11 +24,11 @@ export async function createDatabaseIndex({
}: DatabaseIndexCreateVariables) {
const { schema, entity, type, columns } = payload
- const sql = `
- CREATE INDEX ON ${ident(schema)}.${ident(entity)} USING ${type} (${columns
- .map((column) => ident(column))
- .join(', ')});
- `.trim()
+ const columnList = joinSqlFragments(
+ columns.map((column) => ident(column)),
+ ', '
+ )
+ const sql = safeSql`CREATE INDEX ON ${ident(schema)}.${ident(entity)} USING ${keyword(type)} (${columnList});`
const { result } = await executeSql({
projectRef,
diff --git a/apps/studio/data/database-indexes/index-delete-mutation.ts b/apps/studio/data/database-indexes/index-delete-mutation.ts
index 4f2ff441b4b..5cb809613ae 100644
--- a/apps/studio/data/database-indexes/index-delete-mutation.ts
+++ b/apps/studio/data/database-indexes/index-delete-mutation.ts
@@ -1,4 +1,4 @@
-import { ident } from '@supabase/pg-meta/src/pg-format'
+import { ident, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -19,7 +19,7 @@ export async function deleteDatabaseIndex({
name,
schema,
}: DatabaseIndexDeleteVariables) {
- const sql = `drop index if exists ${ident(schema)}.${ident(name)}`
+ const sql = safeSql`drop index if exists ${ident(schema)}.${ident(name)}`
const { result } = await executeSql({
projectRef,
diff --git a/apps/studio/data/database-integrations/stripe/sync-state-query.ts b/apps/studio/data/database-integrations/stripe/sync-state-query.ts
index 226e304d1f2..9dcd5e5325f 100644
--- a/apps/studio/data/database-integrations/stripe/sync-state-query.ts
+++ b/apps/studio/data/database-integrations/stripe/sync-state-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
import { z } from 'zod'
@@ -30,7 +31,7 @@ export async function getStripeSyncState(
{
projectRef,
connectionString,
- sql: `
+ sql: safeSql`
SELECT started_at, closed_at, status FROM stripe.sync_runs WHERE status != 'pending' ORDER BY started_at DESC LIMIT 1;
`,
queryKey: stripeSyncKeys.syncState(projectRef),
diff --git a/apps/studio/data/database-queues/database-queue-messages-archive-mutation.ts b/apps/studio/data/database-queues/database-queue-messages-archive-mutation.ts
index e9cee99445c..590ea9e10f5 100644
--- a/apps/studio/data/database-queues/database-queue-messages-archive-mutation.ts
+++ b/apps/studio/data/database-queues/database-queue-messages-archive-mutation.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -28,7 +28,7 @@ export async function archiveDatabaseQueueMessage({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `SELECT * FROM pgmq.archive(${literal(queueName)}, ${literal(messageId)})`,
+ sql: safeSql`SELECT * FROM pgmq.archive(${literal(queueName)}, ${literal(messageId)})`,
queryKey: databaseQueuesKeys.create(),
})
diff --git a/apps/studio/data/database-queues/database-queue-messages-delete-mutation.ts b/apps/studio/data/database-queues/database-queue-messages-delete-mutation.ts
index 31efddebde8..fcf4b9c0d91 100644
--- a/apps/studio/data/database-queues/database-queue-messages-delete-mutation.ts
+++ b/apps/studio/data/database-queues/database-queue-messages-delete-mutation.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -29,7 +29,7 @@ export async function deleteDatabaseQueueMessage({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `SELECT * FROM pgmq.delete(${literal(queueName)}, ${literal(messageId)})`,
+ sql: safeSql`SELECT * FROM pgmq.delete(${literal(queueName)}, ${literal(messageId)})`,
queryKey: databaseQueuesKeys.create(),
})
diff --git a/apps/studio/data/database-queues/database-queue-messages-infinite-query.ts b/apps/studio/data/database-queues/database-queue-messages-infinite-query.ts
index f96e55089e9..f49e1ed338e 100644
--- a/apps/studio/data/database-queues/database-queue-messages-infinite-query.ts
+++ b/apps/studio/data/database-queues/database-queue-messages-infinite-query.ts
@@ -1,10 +1,14 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { ident, literal, safeSql, type SafeSqlFragment } from '@supabase/pg-meta/src/pg-format'
import { InfiniteData, useInfiniteQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { last } from 'lodash'
import { databaseQueuesKeys } from './keys'
-import { isQueueNameValid } from '@/components/interfaces/Integrations/Queues/Queues.utils'
+import {
+ isQueueNameValid,
+ pgmqArchiveTable,
+ pgmqQueueTable,
+} from '@/components/interfaces/Integrations/Queues/Queues.utils'
import { QUEUE_MESSAGE_TYPE } from '@/components/interfaces/Integrations/Queues/SingleQueue/Queue.utils'
import { executeSql } from '@/data/sql/execute-sql-query'
import { DATE_FORMAT } from '@/lib/constants'
@@ -47,34 +51,46 @@ export async function getDatabaseQueue({
}
// handles when scheduled and available are deselected
- let queueQuery = ``
+ const queueTable = safeSql`${ident('pgmq')}.${ident(pgmqQueueTable(queueName))}`
+ const archivedTable = safeSql`${ident('pgmq')}.${ident(pgmqArchiveTable(queueName))}`
+ const nowLiteral = literal(dayjs(new Date()).format(DATE_FORMAT))
+
+ let queueQuery: SafeSqlFragment | null = null
if (status.includes('available') && status.includes('scheduled')) {
- queueQuery = `SELECT msg_id, enqueued_at, read_ct, vt, message, NULL as archived_at FROM "pgmq"."q_${queueName}"`
+ queueQuery = safeSql`SELECT msg_id, enqueued_at, read_ct, vt, message, NULL as archived_at FROM ${queueTable}`
} else if (status.includes('available') && !status.includes('scheduled')) {
- queueQuery = `SELECT msg_id, enqueued_at, read_ct, vt, message, NULL as archived_at FROM "pgmq"."q_${queueName}" WHERE vt < '${dayjs(new Date()).format(DATE_FORMAT)}'`
+ queueQuery = safeSql`SELECT msg_id, enqueued_at, read_ct, vt, message, NULL as archived_at FROM ${queueTable} WHERE vt < ${nowLiteral}`
} else if (!status.includes('available') && status.includes('scheduled')) {
- queueQuery = `SELECT msg_id, enqueued_at, read_ct, vt, message, NULL as archived_at FROM "pgmq"."q_${queueName}" WHERE vt > '${dayjs(new Date()).format(DATE_FORMAT)}'`
+ queueQuery = safeSql`SELECT msg_id, enqueued_at, read_ct, vt, message, NULL as archived_at FROM ${queueTable} WHERE vt > ${nowLiteral}`
}
- let archivedQuery = ``
- if (status.includes('archived')) {
- archivedQuery = `SELECT msg_id, enqueued_at, read_ct, vt, message, archived_at FROM "pgmq"."a_${queueName}"`
- }
+ const archivedQuery = status.includes('archived')
+ ? safeSql`SELECT msg_id, enqueued_at, read_ct, vt, message, archived_at FROM ${archivedTable}`
+ : null
- let query = `SELECT
+ const unionParts = [queueQuery, archivedQuery].filter(
+ (part): part is SafeSqlFragment => part !== null
+ )
+ const unionFragment = unionParts.reduce(
+ (acc, part, index) => (index === 0 ? part : safeSql`${acc} UNION ALL ${part}`),
+ safeSql``
+ )
+
+ const whereClause = afterTimestamp
+ ? safeSql` WHERE enqueued_at > ${literal(afterTimestamp)}`
+ : safeSql``
+
+ const sql = safeSql`SELECT
*
FROM
(
- ${[queueQuery, archivedQuery].filter(Boolean).join(' UNION ALL ')}
- ) AS combined`
- if (afterTimestamp) {
- query += ` WHERE enqueued_at > ${literal(afterTimestamp)}`
- }
+ ${unionFragment}
+ ) AS combined${whereClause} order by enqueued_at LIMIT ${literal(QUEUE_MESSAGES_PAGE_SIZE)}`
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `${query} order by enqueued_at LIMIT ${QUEUE_MESSAGES_PAGE_SIZE}`,
+ sql,
})
return result as DatabaseQueueData
}
diff --git a/apps/studio/data/database-queues/database-queue-messages-read-mutation.ts b/apps/studio/data/database-queues/database-queue-messages-read-mutation.ts
index f51bca283b7..7c72f35bc0d 100644
--- a/apps/studio/data/database-queues/database-queue-messages-read-mutation.ts
+++ b/apps/studio/data/database-queues/database-queue-messages-read-mutation.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -31,7 +31,7 @@ export async function readDatabaseQueueMessage({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `select * from pgmq.set_vt(${literal(queueName)}, ${literal(messageId)}, ${literal(duration)})`,
+ sql: safeSql`select * from pgmq.set_vt(${literal(queueName)}, ${literal(messageId)}, ${literal(duration)})`,
queryKey: databaseQueuesKeys.create(),
})
diff --git a/apps/studio/data/database-queues/database-queue-messages-send-mutation.ts b/apps/studio/data/database-queues/database-queue-messages-send-mutation.ts
index 4b181760e44..a3f6218fba2 100644
--- a/apps/studio/data/database-queues/database-queue-messages-send-mutation.ts
+++ b/apps/studio/data/database-queues/database-queue-messages-send-mutation.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -31,7 +31,7 @@ export async function sendDatabaseQueueMessage({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `select * from pgmq.send(${literal(queueName)}, ${literal(payload)}, ${literal(delay)})`,
+ sql: safeSql`select * from pgmq.send(${literal(queueName)}, ${literal(payload)}, ${literal(delay)})`,
queryKey: databaseQueuesKeys.create(),
})
diff --git a/apps/studio/data/database-queues/database-queues-create-mutation.ts b/apps/studio/data/database-queues/database-queues-create-mutation.ts
index c5c02394c3d..e26120ca0ae 100644
--- a/apps/studio/data/database-queues/database-queues-create-mutation.ts
+++ b/apps/studio/data/database-queues/database-queues-create-mutation.ts
@@ -1,9 +1,12 @@
-import { ident, literal } from '@supabase/pg-meta/src/pg-format'
+import { ident, literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { databaseQueuesKeys } from './keys'
-import { isQueueNameValid } from '@/components/interfaces/Integrations/Queues/Queues.utils'
+import {
+ isQueueNameValid,
+ pgmqQueueTable,
+} from '@/components/interfaces/Integrations/Queues/Queues.utils'
import { executeSql } from '@/data/sql/execute-sql-query'
import { tableKeys } from '@/data/tables/keys'
import type { ResponseError, UseCustomMutationOptions } from '@/types'
@@ -36,17 +39,21 @@ export async function createDatabaseQueue({
const { partitionInterval, retentionInterval } = configuration ?? {}
- const query =
+ const createFragment =
type === 'partitioned'
- ? `select from pgmq.create_partitioned(${literal(name)}, ${literal(partitionInterval)}, ${literal(retentionInterval)});`
+ ? safeSql`select from pgmq.create_partitioned(${literal(name)}, ${literal(partitionInterval)}, ${literal(retentionInterval)});`
: type === 'unlogged'
- ? `SELECT pgmq.create_unlogged(${literal(name)});`
- : `SELECT pgmq.create(${literal(name)});`
+ ? safeSql`SELECT pgmq.create_unlogged(${literal(name)});`
+ : safeSql`SELECT pgmq.create(${literal(name)});`
+
+ const rlsFragment = enableRls
+ ? safeSql` alter table ${ident('pgmq')}.${ident(pgmqQueueTable(name))} enable row level security;`
+ : safeSql``
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `${query} ${enableRls ? `alter table pgmq.${ident('q_' + name)} enable row level security;` : ''}`.trim(),
+ sql: safeSql`${createFragment}${rlsFragment}`,
queryKey: databaseQueuesKeys.create(),
})
diff --git a/apps/studio/data/database-queues/database-queues-delete-mutation.ts b/apps/studio/data/database-queues/database-queues-delete-mutation.ts
index 97bac6182e2..38fc436080b 100644
--- a/apps/studio/data/database-queues/database-queues-delete-mutation.ts
+++ b/apps/studio/data/database-queues/database-queues-delete-mutation.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -27,7 +27,7 @@ export async function deleteDatabaseQueue({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `select * from pgmq.drop_queue(${literal(queueName)});`,
+ sql: safeSql`select * from pgmq.drop_queue(${literal(queueName)});`,
queryKey: databaseQueuesKeys.delete(queueName),
})
diff --git a/apps/studio/data/database-queues/database-queues-metrics-query.ts b/apps/studio/data/database-queues/database-queues-metrics-query.ts
index 82dbe4c5f0f..a41a6586a71 100644
--- a/apps/studio/data/database-queues/database-queues-metrics-query.ts
+++ b/apps/studio/data/database-queues/database-queues-metrics-query.ts
@@ -1,8 +1,11 @@
-import { ident, literal } from '@supabase/pg-meta/src/pg-format'
+import { ident, literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { databaseQueuesKeys } from './keys'
-import { isQueueNameValid } from '@/components/interfaces/Integrations/Queues/Queues.utils'
+import {
+ isQueueNameValid,
+ pgmqQueueTable,
+} from '@/components/interfaces/Integrations/Queues/Queues.utils'
import { executeSql } from '@/data/sql/execute-sql-query'
import type { ResponseError, UseCustomQueryOptions } from '@/types'
@@ -18,27 +21,25 @@ export type PostgresQueueMetric = {
method: 'estimated' | 'precise'
}
-const preciseMetricsSqlQuery = (queueName: string) => {
- return `
+const preciseMetricsSqlQuery = (queueName: string) =>
+ safeSql`
set local statement_timeout = '1s';
SELECT
COUNT(*) AS row_count
FROM
- "pgmq".${ident('q_' + queueName)};
+ ${ident('pgmq')}.${ident(pgmqQueueTable(queueName))};
`
-}
-const estimateMetricsSqlQuery = (queueName: string) => {
- return `
+const estimateMetricsSqlQuery = (queueName: string) =>
+ safeSql`
select
reltuples::bigint as estimated_rows
from
pg_class
where
- relname = ${literal('q_' + queueName)}
+ relname = ${literal(pgmqQueueTable(queueName))}
and relnamespace = 'pgmq'::regnamespace;
`
-}
export async function getDatabaseQueuesMetrics({
projectRef,
diff --git a/apps/studio/data/database-queues/database-queues-purge-mutation.ts b/apps/studio/data/database-queues/database-queues-purge-mutation.ts
index 4d42427dd34..dc9aa97ae69 100644
--- a/apps/studio/data/database-queues/database-queues-purge-mutation.ts
+++ b/apps/studio/data/database-queues/database-queues-purge-mutation.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
@@ -27,7 +27,7 @@ export async function purgeDatabaseQueue({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `select * from pgmq.purge_queue(${literal(queueName)});`,
+ sql: safeSql`select * from pgmq.purge_queue(${literal(queueName)});`,
queryKey: databaseQueuesKeys.purge(queueName),
})
diff --git a/apps/studio/data/database/retrieve-index-from-select-query.ts b/apps/studio/data/database/retrieve-index-from-select-query.ts
index 8ed2108728e..419ecd6e323 100644
--- a/apps/studio/data/database/retrieve-index-from-select-query.ts
+++ b/apps/studio/data/database/retrieve-index-from-select-query.ts
@@ -1,3 +1,4 @@
+import { joinSqlFragments, literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { databaseKeys } from './keys'
@@ -31,7 +32,7 @@ export async function getInvolvedIndexesInSelectQuery({
projectRef,
connectionString,
queryKey: ['involved-indexes-explain-query'],
- sql: /* sql */ `
+ sql: safeSql`
create or replace function pg_temp.explain_query(query text) returns jsonb
language plpgsql
as $$
@@ -101,7 +102,7 @@ export async function getInvolvedIndexesInSelectQuery({
end;
$$;
- select pg_temp.explain_query('${query}') as plans;
+ select pg_temp.explain_query(${literal(query)}) as plans;
`,
})
@@ -113,7 +114,7 @@ export async function getInvolvedIndexesInSelectQuery({
projectRef,
connectionString,
queryKey: ['involved-indexes-names'],
- sql: `select schemaname as schema, tablename as table, indexname as name from pg_indexes where indexname in (${involvedIndexes.map((name) => `'${name}'`).join(', ')});`,
+ sql: safeSql`select schemaname as schema, tablename as table, indexname as name from pg_indexes where indexname in (${joinSqlFragments(involvedIndexes.map(literal), ', ')});`,
})
return indexResult as GetInvolvedIndexesFromSelectQueryResponse[]
diff --git a/apps/studio/data/database/supamonitor-enabled-query.ts b/apps/studio/data/database/supamonitor-enabled-query.ts
index e0670f12213..ed1046325cf 100644
--- a/apps/studio/data/database/supamonitor-enabled-query.ts
+++ b/apps/studio/data/database/supamonitor-enabled-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { databaseKeys } from './keys'
@@ -18,7 +19,7 @@ export async function getSupamonitorEnabled({
const { result } = await executeSql<{ libraries: string }[]>({
projectRef,
connectionString,
- sql: `SELECT current_setting('shared_preload_libraries', true) AS libraries`,
+ sql: safeSql`SELECT current_setting('shared_preload_libraries', true) AS libraries`,
})
const libraries = result[0]?.libraries ?? ''
diff --git a/apps/studio/data/storage/public-buckets-with-select-policies-query.ts b/apps/studio/data/storage/public-buckets-with-select-policies-query.ts
index fa6cee9c404..3df8fb7a3f4 100644
--- a/apps/studio/data/storage/public-buckets-with-select-policies-query.ts
+++ b/apps/studio/data/storage/public-buckets-with-select-policies-query.ts
@@ -1,4 +1,4 @@
-import { literal } from '@supabase/pg-meta/src/pg-format'
+import { literal, safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { storageKeys } from './keys'
@@ -36,7 +36,7 @@ async function getPublicBucketsWithSelectPolicies({
const { result } = await executeSql({
projectRef,
connectionString,
- sql: `
+ sql: safeSql`
SELECT b.id AS bucket_id, b.name AS bucket_name, p.policyname
FROM storage.buckets b
JOIN pg_policies p
diff --git a/apps/studio/data/tables/table-names-query.ts b/apps/studio/data/tables/table-names-query.ts
index a55053bf4e6..6bbbd0f226e 100644
--- a/apps/studio/data/tables/table-names-query.ts
+++ b/apps/studio/data/tables/table-names-query.ts
@@ -1,3 +1,4 @@
+import { safeSql } from '@supabase/pg-meta/src/pg-format'
import { useQuery } from '@tanstack/react-query'
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
@@ -15,7 +16,7 @@ export type TableNamesVariables = {
connectionString?: string | null
}
-const TABLE_NAMES_SQL = /* sql */ `
+const TABLE_NAMES_SQL = safeSql`
select
c.oid::int8 as id,
nc.nspname as schema,
@@ -31,7 +32,7 @@ where c.relkind in ('r', 'p')
or has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES')
)
order by nc.nspname, c.relname
-`.trim()
+`
export async function getTableNames(
{ projectRef, connectionString }: TableNamesVariables,
diff --git a/packages/pg-meta/src/sql/studio/database/types.ts b/packages/pg-meta/src/sql/studio/database/types.ts
index 2aea7a941d8..74f756bc519 100644
--- a/packages/pg-meta/src/sql/studio/database/types.ts
+++ b/packages/pg-meta/src/sql/studio/database/types.ts
@@ -22,7 +22,7 @@ export const getCreateEnumeratedTypeSQL = ({
}
export const getDeleteEnumeratedTypeSQL = ({ schema, name }: { schema: string; name: string }) => {
- return `drop type if exists ${ident(schema)}.${ident(name)}`
+ return safeSql`drop type if exists ${ident(schema)}.${ident(name)}`
}
export const getUpdateEnumeratedTypeSQL = ({