chore: remove useExecuteSqlQuery() part 2 (#30467)
* foreign-key-constraints * update entity-types stale time * schemas query * deprecate useExecuteSqlQuery * users count query * database size query * indexes query * keywords query * migrations query * table columns * database functions * database roles query * fdws query * replication lag query * ongoing queries query * vault secrets query * remove unneeded staleTime: 0 * max connections query * fix entity types key in tests * Some fixes --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
@@ -123,8 +123,7 @@ export const UsersV2 = () => {
|
||||
providers: selectedProviders,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const totalUsers = useMemo(() => countData?.result[0].count ?? 0, [countData?.result[0].count])
|
||||
const totalUsers = countData ?? 0
|
||||
const users = useMemo(() => data?.pages.flatMap((page) => page.result) ?? [], [data?.pages])
|
||||
|
||||
const handleScroll = (event: UIEvent<HTMLDivElement>) => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { Check, ChevronsUpDown, Loader2 } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
@@ -6,7 +7,7 @@ import { toast } from 'sonner'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import CodeEditor from 'components/ui/CodeEditor/CodeEditor'
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useIndexesQuery } from 'data/database/indexes-query'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useTableColumnsQuery } from 'data/database/table-columns-query'
|
||||
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
|
||||
@@ -43,6 +44,7 @@ interface CreateIndexSidePanelProps {
|
||||
}
|
||||
|
||||
const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) => {
|
||||
const queryClient = useQueryClient()
|
||||
const { project } = useProjectContext()
|
||||
const [selectedSchema, setSelectedSchema] = useState('public')
|
||||
const [selectedEntity, setSelectedEntity] = useState<string | undefined>(undefined)
|
||||
@@ -51,11 +53,7 @@ const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) =
|
||||
const [schemaDropdownOpen, setSchemaDropdownOpen] = useState(false)
|
||||
const [tableDropdownOpen, setTableDropdownOpen] = useState(false)
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const { refetch: refetchIndexes } = useIndexesQuery({
|
||||
schema: selectedSchema,
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const { data: schemas } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
@@ -80,7 +78,7 @@ const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) =
|
||||
|
||||
const { mutate: execute, isLoading: isExecuting } = useExecuteSqlMutation({
|
||||
onSuccess: async () => {
|
||||
await refetchIndexes()
|
||||
await queryClient.invalidateQueries(databaseKeys.indexes(project?.ref, selectedSchema))
|
||||
onClose()
|
||||
toast.success(`Successfully created index`)
|
||||
},
|
||||
@@ -98,7 +96,7 @@ const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) =
|
||||
setSearchTerm(value)
|
||||
}
|
||||
|
||||
const columns = tableColumns?.result[0]?.columns ?? []
|
||||
const columns = tableColumns?.[0]?.columns ?? []
|
||||
const columnOptions: MultiSelectOption[] = columns
|
||||
.filter((column): column is NonNullable<typeof column> => column !== null)
|
||||
.map((column) => ({
|
||||
|
||||
@@ -53,12 +53,12 @@ const Indexes = () => {
|
||||
})
|
||||
|
||||
const { mutate: execute, isLoading: isExecuting } = useExecuteSqlMutation({
|
||||
onSuccess() {
|
||||
refetchIndexes()
|
||||
onSuccess: async () => {
|
||||
await refetchIndexes()
|
||||
setSelectedIndexToDelete(undefined)
|
||||
toast.success('Successfully deleted index')
|
||||
},
|
||||
onError(error) {
|
||||
onError: (error) => {
|
||||
toast.error(`Failed to delete index: ${error.message}`)
|
||||
},
|
||||
})
|
||||
@@ -69,7 +69,7 @@ const Indexes = () => {
|
||||
const schema = schemas?.find((schema) => schema.name === selectedSchema)
|
||||
const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
|
||||
|
||||
const sortedIndexes = sortBy(allIndexes?.result ?? [], (index) => index.name.toLocaleLowerCase())
|
||||
const sortedIndexes = sortBy(allIndexes ?? [], (index) => index.name.toLocaleLowerCase())
|
||||
const indexes =
|
||||
search.length > 0
|
||||
? sortedIndexes.filter((index) => index.name.includes(search) || index.table.includes(search))
|
||||
|
||||
@@ -22,8 +22,8 @@ const Migrations = () => {
|
||||
})
|
||||
const migrations =
|
||||
search.length === 0
|
||||
? data?.result ?? []
|
||||
: data?.result.filter(
|
||||
? data ?? []
|
||||
: data?.filter(
|
||||
(migration) => migration.version.includes(search) || migration.name?.includes(search)
|
||||
) ?? []
|
||||
|
||||
@@ -62,9 +62,9 @@ const Migrations = () => {
|
||||
)}
|
||||
{isSuccess && (
|
||||
<div>
|
||||
{data.result.length <= 0 && <MigrationsEmptyState />}
|
||||
{data.length <= 0 && <MigrationsEmptyState />}
|
||||
|
||||
{data.result.length > 0 && (
|
||||
{data.length > 0 && (
|
||||
<>
|
||||
<div className="w-80 mb-4">
|
||||
<Input
|
||||
|
||||
@@ -16,6 +16,7 @@ import DeleteRoleModal from './DeleteRoleModal'
|
||||
import RoleRow from './RoleRow'
|
||||
import RoleRowSkeleton from './RoleRowSkeleton'
|
||||
import { SUPABASE_ROLES } from './Roles.constants'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
|
||||
type SUPABASE_ROLE = (typeof SUPABASE_ROLES)[number]
|
||||
|
||||
@@ -151,73 +152,60 @@ const RolesList = () => {
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger asChild>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!canUpdateRoles}
|
||||
icon={<Plus size={12} />}
|
||||
onClick={() => setIsCreatingRole(true)}
|
||||
>
|
||||
Add role
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
{!canUpdateRoles && (
|
||||
<Tooltip.Content align="start" side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
<div
|
||||
className={[
|
||||
'rounded bg-alternative py-1 px-2 leading-none shadow',
|
||||
'border border-background text-xs',
|
||||
].join(' ')}
|
||||
>
|
||||
You need additional permissions to add a new role
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
)}
|
||||
</Tooltip.Root>
|
||||
<ButtonTooltip
|
||||
type="primary"
|
||||
disabled={!canUpdateRoles}
|
||||
icon={<Plus size={12} />}
|
||||
onClick={() => setIsCreatingRole(true)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !canUpdateRoles
|
||||
? 'You need additional permissions to add a new role'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Add role
|
||||
</ButtonTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{supabaseRoles.length > 0 && (
|
||||
<div>
|
||||
<div className="bg-surface-100 border border-default px-6 py-3 rounded-t flex items-center space-x-4">
|
||||
<p className="text-sm text-foreground-light">Roles managed by Supabase</p>
|
||||
<Badge variant="brand">Protected</Badge>
|
||||
</div>
|
||||
|
||||
{isLoading
|
||||
? Array.from({ length: 5 }).map((_, i) => <RoleRowSkeleton key={i} index={i} />)
|
||||
: supabaseRoles.map((role) => (
|
||||
<RoleRow
|
||||
disabled
|
||||
key={role.id}
|
||||
role={role}
|
||||
onSelectDelete={setSelectedRoleToDelete}
|
||||
/>
|
||||
))}
|
||||
<div>
|
||||
<div className="bg-surface-100 border border-default px-6 py-3 rounded-t flex items-center space-x-4">
|
||||
<p className="text-sm text-foreground-light">Roles managed by Supabase</p>
|
||||
<Badge variant="brand">Protected</Badge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{otherRoles.length > 0 && (
|
||||
<div>
|
||||
<div className="bg-surface-100 border border-default px-6 py-3 rounded-t">
|
||||
<p className="text-sm text-foreground-light">Other database roles</p>
|
||||
</div>
|
||||
{isLoading
|
||||
? Array.from({ length: 5 }).map((_, i) => <RoleRowSkeleton key={i} index={i} />)
|
||||
: supabaseRoles.map((role) => (
|
||||
<RoleRow
|
||||
disabled
|
||||
key={role.id}
|
||||
role={role}
|
||||
onSelectDelete={setSelectedRoleToDelete}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{isLoading
|
||||
? Array.from({ length: 3 }).map((_, i) => <RoleRowSkeleton key={i} index={i} />)
|
||||
: otherRoles.map((role) => (
|
||||
<RoleRow
|
||||
key={role.id}
|
||||
disabled={!canUpdateRoles}
|
||||
role={role}
|
||||
onSelectDelete={setSelectedRoleToDelete}
|
||||
/>
|
||||
))}
|
||||
<div>
|
||||
<div className="bg-surface-100 border border-default px-6 py-3 rounded-t">
|
||||
<p className="text-sm text-foreground-light">Other database roles</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading
|
||||
? Array.from({ length: 3 }).map((_, i) => <RoleRowSkeleton key={i} index={i} />)
|
||||
: otherRoles.map((role) => (
|
||||
<RoleRow
|
||||
key={role.id}
|
||||
disabled={!canUpdateRoles}
|
||||
role={role}
|
||||
onSelectDelete={setSelectedRoleToDelete}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{filterString.length > 0 && filteredRoles.length === 0 && (
|
||||
|
||||
@@ -49,7 +49,7 @@ const EditWrapper = () => {
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const wrappers = data?.result ?? []
|
||||
const wrappers = data ?? []
|
||||
const foundWrapper = wrappers.find((w) => Number(w.id) === Number(id))
|
||||
// this call to useImmutableValue should be removed if the redirect after update is also removed
|
||||
const wrapper = useImmutableValue(foundWrapper)
|
||||
|
||||
@@ -19,7 +19,7 @@ const Wrappers = ({ isEnabled }: { isEnabled: boolean }) => {
|
||||
const [open, setOpen] = useState<string>('')
|
||||
const [selectedWrapperToDelete, setSelectedWrapperToDelete] = useState<FDW>()
|
||||
|
||||
const wrappers = data?.result ?? []
|
||||
const wrappers = data ?? []
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { UseFormReturn } from 'react-hook-form'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { useRemainingDurationForDiskAttributeUpdate } from 'data/config/disk-attributes-query'
|
||||
import { useDiskUtilizationQuery } from 'data/config/disk-utilization-query'
|
||||
import { useDatabaseSizeQuery } from 'data/database/database-size-query'
|
||||
import { GB } from 'lib/constants'
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
} from 'ui'
|
||||
import { DiskStorageSchemaType } from '../DiskManagement.schema'
|
||||
import { AUTOSCALING_THRESHOLD } from './DiskManagement.constants'
|
||||
import { useRemainingDurationForDiskAttributeUpdate } from 'data/config/disk-attributes-query'
|
||||
|
||||
interface DiskSpaceBarProps {
|
||||
form: UseFormReturn<DiskStorageSchemaType>
|
||||
@@ -56,7 +56,7 @@ export default function DiskSpaceBar({ form }: DiskSpaceBarProps) {
|
||||
})
|
||||
const { remainingDuration } = useRemainingDurationForDiskAttributeUpdate({ projectRef: ref })
|
||||
|
||||
const databaseSizeBytes = data?.result[0].db_size ?? 0
|
||||
const databaseSizeBytes = data ?? 0
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center h-6 gap-3">
|
||||
|
||||
@@ -62,7 +62,7 @@ const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfigurationProps)
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const databaseSizeBytesUsed = data?.result[0].db_size ?? 0
|
||||
const databaseSizeBytesUsed = data ?? 0
|
||||
|
||||
return (
|
||||
<div id="diskManagement">
|
||||
|
||||
@@ -103,9 +103,7 @@ const SecretRow = ({ secret, onSelectEdit, onSelectRemove }: SecretRowProps) =>
|
||||
</p>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" className="px-1">
|
||||
<MoreVertical />
|
||||
</Button>
|
||||
<Button type="text" className="px-1" icon={<MoreVertical />} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="bottom">
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
|
||||
@@ -12,7 +12,6 @@ import type { Constraint } from 'data/database/constraints-query'
|
||||
import type { ForeignKeyConstraint } from 'data/database/foreign-key-constraints-query'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { entityTypeKeys } from 'data/entity-types/keys'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { tableEditorKeys } from 'data/table-editor/keys'
|
||||
import { isTableLike } from 'data/table-editor/table-editor-types'
|
||||
import { tableRowKeys } from 'data/table-rows/keys'
|
||||
@@ -258,7 +257,9 @@ const SidePanelEditor = ({
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(tableEditorKeys.tableEditor(project?.ref, selectedTable?.id)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(project?.ref, ['foreign-key-constraints'])),
|
||||
queryClient.invalidateQueries(
|
||||
databaseKeys.foreignKeyConstraints(project?.ref, selectedTable?.schema)
|
||||
),
|
||||
queryClient.invalidateQueries(
|
||||
databaseKeys.tableDefinition(project?.ref, selectedTable?.id)
|
||||
),
|
||||
|
||||
@@ -16,7 +16,6 @@ import { entityTypeKeys } from 'data/entity-types/keys'
|
||||
import { prefetchEditorTablePage } from 'data/prefetchers/project.$ref.editor.$id'
|
||||
import { getQueryClient } from 'data/query-client'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { tableEditorKeys } from 'data/table-editor/keys'
|
||||
import { prefetchTableEditor } from 'data/table-editor/table-editor-query'
|
||||
import { tableRowKeys } from 'data/table-rows/keys'
|
||||
@@ -760,7 +759,7 @@ export const updateTable = async ({
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(tableEditorKeys.tableEditor(projectRef, table.id)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, ['foreign-key-constraints'])),
|
||||
queryClient.invalidateQueries(databaseKeys.foreignKeyConstraints(projectRef, table.schema)),
|
||||
queryClient.invalidateQueries(databaseKeys.tableDefinition(projectRef, table.id)),
|
||||
queryClient.invalidateQueries(entityTypeKeys.list(projectRef)),
|
||||
])
|
||||
|
||||
@@ -77,7 +77,7 @@ export function useDatabaseGotoCommands(options?: CommandOptions) {
|
||||
id: 'nav-database-hooks',
|
||||
name: 'Webhooks',
|
||||
value: 'Database: Webhooks',
|
||||
route: `/project/${ref}/database/hooks`,
|
||||
route: `/project/${ref}/integrations/hooks`,
|
||||
defaultHidden: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ export function useDatabaseGotoCommands(options?: CommandOptions) {
|
||||
id: 'nav-database-hooks',
|
||||
name: 'Webhooks',
|
||||
value: 'Database: Webhooks',
|
||||
route: `/project/${ref}/database/hooks`,
|
||||
route: `/project/${ref}/integrations/hooks`,
|
||||
defaultHidden: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,8 +17,8 @@ import Results from 'components/interfaces/SQLEditor/UtilityPanel/Results'
|
||||
import { useSqlDebugMutation } from 'data/ai/sql-debug-mutation'
|
||||
import { databasePoliciesKeys } from 'data/database-policies/keys'
|
||||
import { useEntityDefinitionQuery } from 'data/database/entity-definition-query'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { QueryResponseError, useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { usePrevious } from 'hooks/deprecated'
|
||||
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
|
||||
@@ -113,7 +113,7 @@ export const AiAssistantPanel = () => {
|
||||
if (editor !== null) {
|
||||
switch (editor) {
|
||||
case 'functions':
|
||||
await queryClient.invalidateQueries(sqlKeys.query(ref, ['functions-list']))
|
||||
await queryClient.invalidateQueries(databaseKeys.databaseFunctions(ref))
|
||||
break
|
||||
case 'rls-policies':
|
||||
await queryClient.invalidateQueries(databasePoliciesKeys.list(ref))
|
||||
|
||||
@@ -11,9 +11,7 @@ Referencing the other files in the `data` directory is a good way to see how to
|
||||
|
||||
### SQL queries and mutations
|
||||
|
||||
The dashboard often needs to query the users database directly. There are already some reusable hooks for this in `data/sql`. Reference the `data/fdw` directory for an example of how to use them.
|
||||
|
||||
Note that the query key of the `useExecuteSqlQuery` hook will be automatically filled with an md5 hash of the SQL query, unless you provide a name for the query.
|
||||
The dashboard often needs to query the users database directly. You can use the `executeSql()` function to do this.
|
||||
|
||||
## Files
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { configKeys } from 'data/config/keys'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { handleError, patch } from 'data/fetchers'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
|
||||
export type CreateAndExposeAPISchemaVariables = {
|
||||
@@ -69,7 +69,7 @@ export const useCreateAndExposeAPISchemaMutation = ({
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, ['schemas', 'list'])),
|
||||
queryClient.invalidateQueries(databaseKeys.schemas(projectRef)),
|
||||
queryClient.invalidateQueries(configKeys.postgrest(projectRef)),
|
||||
])
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -6,7 +6,7 @@ export const authKeys = {
|
||||
keywords: string | undefined
|
||||
filter: string | undefined
|
||||
}
|
||||
) => ['auth', projectRef, 'users', ...(params ? [params] : [])] as const,
|
||||
) => ['projects', projectRef, 'users', ...(params ? [params] : [])] as const,
|
||||
|
||||
usersInfinite: (
|
||||
projectRef: string | undefined,
|
||||
@@ -17,7 +17,13 @@ export const authKeys = {
|
||||
sort: string | undefined
|
||||
order: string | undefined
|
||||
}
|
||||
) => ['auth', projectRef, 'users-infinite', ...(params ? [params].filter(Boolean) : [])] as const,
|
||||
) =>
|
||||
[
|
||||
'projects',
|
||||
projectRef,
|
||||
'users-infinite',
|
||||
...(params ? [params].filter(Boolean) : []),
|
||||
] as const,
|
||||
usersCount: (
|
||||
projectRef: string | undefined,
|
||||
params?: {
|
||||
@@ -25,8 +31,9 @@ export const authKeys = {
|
||||
filter: string | undefined
|
||||
providers: string[] | undefined
|
||||
}
|
||||
) => ['auth', projectRef, 'users-count', ...(params ? [params].filter(Boolean) : [])] as const,
|
||||
) =>
|
||||
['projects', projectRef, 'users-count', ...(params ? [params].filter(Boolean) : [])] as const,
|
||||
|
||||
authConfig: (projectRef: string | undefined) => ['auth', projectRef, 'config'] as const,
|
||||
authConfig: (projectRef: string | undefined) => ['projects', projectRef, 'auth-config'] as const,
|
||||
accessToken: () => ['access-token'] as const,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { handleError, post } from 'data/fetchers'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { authKeys } from './keys'
|
||||
|
||||
@@ -46,9 +45,9 @@ export const useUserCreateMutation = ({
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(authKeys.usersInfinite(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, authKeys.usersCount(projectRef))),
|
||||
queryClient.invalidateQueries(authKeys.usersCount(projectRef)),
|
||||
])
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { del, handleError } from 'data/fetchers'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { authKeys } from './keys'
|
||||
import type { User } from './users-infinite-query'
|
||||
@@ -41,7 +40,7 @@ export const useUserDeleteMutation = ({
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(authKeys.usersInfinite(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, authKeys.usersCount(projectRef))),
|
||||
queryClient.invalidateQueries(authKeys.usersCount(projectRef)),
|
||||
])
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -2,8 +2,6 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { handleError, post } from 'data/fetchers'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import type { ResponseError } from 'types'
|
||||
import { authKeys } from './keys'
|
||||
|
||||
@@ -41,7 +39,7 @@ export const useUserInviteMutation = ({
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(authKeys.usersInfinite(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, authKeys.usersCount(projectRef))),
|
||||
queryClient.invalidateQueries(authKeys.usersCount(projectRef)),
|
||||
])
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from 'data/sql/execute-sql-query'
|
||||
import { executeSql, ExecuteSqlError } from 'data/sql/execute-sql-query'
|
||||
import { authKeys } from './keys'
|
||||
import { Filter } from './users-infinite-query'
|
||||
|
||||
@@ -12,12 +12,12 @@ type UsersCountVariables = {
|
||||
providers?: string[]
|
||||
}
|
||||
|
||||
const getUsersCountSQl = ({
|
||||
verified,
|
||||
const getUsersCountSql = ({
|
||||
filter,
|
||||
keywords,
|
||||
providers,
|
||||
}: {
|
||||
verified?: Filter
|
||||
filter?: Filter
|
||||
keywords?: string
|
||||
providers?: string[]
|
||||
}) => {
|
||||
@@ -32,11 +32,11 @@ const getUsersCountSQl = ({
|
||||
)
|
||||
}
|
||||
|
||||
if (verified === 'verified') {
|
||||
if (filter === 'verified') {
|
||||
conditions.push(`email_confirmed_at IS NOT NULL or phone_confirmed_at IS NOT NULL`)
|
||||
} else if (verified === 'anonymous') {
|
||||
} else if (filter === 'anonymous') {
|
||||
conditions.push(`is_anonymous is true`)
|
||||
} else if (verified === 'unverified') {
|
||||
} else if (filter === 'unverified') {
|
||||
conditions.push(`email_confirmed_at IS NULL AND phone_confirmed_at IS NULL`)
|
||||
}
|
||||
|
||||
@@ -59,20 +59,44 @@ const getUsersCountSQl = ({
|
||||
return `${baseQueryCount}${conditions.length > 0 ? ` where ${combinedConditions}` : ''};`
|
||||
}
|
||||
|
||||
export type UsersCountData = { result: [{ count: number }] }
|
||||
export type UsersCountError = ExecuteSqlError
|
||||
|
||||
export const useUsersCountQuery = <TData extends UsersCountData = UsersCountData>(
|
||||
export async function getUsersCount(
|
||||
{ projectRef, connectionString, keywords, filter, providers }: UsersCountVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, UsersCountError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getUsersCountSql({ filter, keywords, providers })
|
||||
|
||||
const { result } = await executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getUsersCountSQl({ keywords, verified: filter, providers }),
|
||||
queryKey: authKeys.usersCount(projectRef, { keywords, filter, providers }),
|
||||
sql,
|
||||
queryKey: ['users-count'],
|
||||
},
|
||||
options
|
||||
signal
|
||||
)
|
||||
|
||||
const count = result?.[0]?.count
|
||||
|
||||
if (typeof count !== 'number') {
|
||||
throw new Error('Error fetching users count')
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
export type UsersCountData = Awaited<ReturnType<typeof getUsersCount>>
|
||||
export type UsersCountError = ExecuteSqlError
|
||||
|
||||
export const useUsersCountQuery = <TData = UsersCountData>(
|
||||
{ projectRef, connectionString, keywords, filter, providers }: UsersCountVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<UsersCountData, UsersCountError, TData> = {}
|
||||
) =>
|
||||
useQuery<UsersCountData, UsersCountError, TData>(
|
||||
authKeys.usersCount(projectRef, { keywords, filter, providers }),
|
||||
({ signal }) =>
|
||||
getUsersCount({ projectRef, connectionString, keywords, filter, providers }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -109,9 +109,7 @@ export const useUsersInfiniteQuery = <TData = UsersData>(
|
||||
)
|
||||
},
|
||||
{
|
||||
staleTime: 0,
|
||||
enabled: enabled && typeof projectRef !== 'undefined' && isActive,
|
||||
|
||||
getNextPageParam(lastPage, pages) {
|
||||
const page = pages.length
|
||||
const hasNextPage = lastPage.result.length <= USERS_PAGE_LIMIT
|
||||
|
||||
@@ -4,7 +4,6 @@ import { toast } from 'sonner'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { entityTypeKeys } from 'data/entity-types/keys'
|
||||
import { del, handleError } from 'data/fetchers'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { tableEditorKeys } from 'data/table-editor/keys'
|
||||
import { tableRowKeys } from 'data/table-rows/keys'
|
||||
import { viewKeys } from 'data/views/keys'
|
||||
@@ -58,11 +57,13 @@ export const useDatabaseColumnDeleteMutation = ({
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef, table } = variables
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, ['foreign-key-constraints'])),
|
||||
// refetch all entities in the sidebar because deleting a column may regenerate a view (and change its id)
|
||||
queryClient.invalidateQueries(entityTypeKeys.list(projectRef)),
|
||||
...(table !== undefined
|
||||
? [
|
||||
queryClient.invalidateQueries(
|
||||
databaseKeys.foreignKeyConstraints(projectRef, table?.schema)
|
||||
),
|
||||
queryClient.invalidateQueries(tableEditorKeys.tableEditor(projectRef, table.id)),
|
||||
queryClient.invalidateQueries(databaseKeys.tableDefinition(projectRef, table.id)),
|
||||
// invalidate all views from this schema, not sure if this is needed since you can't actually delete a column
|
||||
|
||||
@@ -3,8 +3,8 @@ import { toast } from 'sonner'
|
||||
import { z } from 'zod'
|
||||
|
||||
import pgMeta from '@supabase/pg-meta'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
|
||||
export type DatabaseFunctionCreateVariables = {
|
||||
@@ -47,7 +47,7 @@ export const useDatabaseFunctionCreateMutation = ({
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await queryClient.invalidateQueries(sqlKeys.query(projectRef, ['functions-list']))
|
||||
await queryClient.invalidateQueries(databaseKeys.databaseFunctions(projectRef))
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { toast } from 'sonner'
|
||||
import { z } from 'zod'
|
||||
|
||||
import pgMeta from '@supabase/pg-meta'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { DatabaseFunction } from './database-functions-query'
|
||||
|
||||
@@ -48,7 +48,7 @@ export const useDatabaseFunctionDeleteMutation = ({
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await queryClient.invalidateQueries(sqlKeys.query(projectRef, ['functions-list']))
|
||||
await queryClient.invalidateQueries(databaseKeys.databaseFunctions(projectRef))
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pgMeta from '@supabase/pg-meta'
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import type { ExecuteSqlData, ExecuteSqlError } from 'data/sql/execute-sql-query'
|
||||
import { useExecuteSqlQuery } from 'data/sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import type { ResponseError } from 'types'
|
||||
import { z } from 'zod'
|
||||
|
||||
@@ -14,27 +14,38 @@ export type DatabaseFunction = z.infer<typeof pgMeta.functions.pgFunctionZod>
|
||||
|
||||
const pgMetaFunctionsList = pgMeta.functions.list()
|
||||
|
||||
export type DatabaseFunctionsData = z.infer<typeof pgMetaFunctionsList.zod>
|
||||
export type DatabaseFunctionsError = ResponseError
|
||||
|
||||
export const useDatabaseFunctionsQuery = <
|
||||
TData extends DatabaseFunctionsData = DatabaseFunctionsData,
|
||||
>(
|
||||
export async function getDatabaseFunctions(
|
||||
{ projectRef, connectionString }: DatabaseFunctionsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, ExecuteSqlError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const { result } = await executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: pgMetaFunctionsList.sql,
|
||||
queryKey: ['functions-list'],
|
||||
queryKey: ['database-functions'],
|
||||
},
|
||||
signal
|
||||
)
|
||||
|
||||
return result as DatabaseFunction[]
|
||||
}
|
||||
|
||||
export type DatabaseFunctionsData = z.infer<typeof pgMetaFunctionsList.zod>
|
||||
export type DatabaseFunctionsError = ResponseError
|
||||
|
||||
export const useDatabaseFunctionsQuery = <TData = DatabaseFunctionsData>(
|
||||
{ projectRef, connectionString }: DatabaseFunctionsVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<DatabaseFunctionsData, DatabaseFunctionsError, TData> = {}
|
||||
) =>
|
||||
useQuery<DatabaseFunctionsData, DatabaseFunctionsError, TData>(
|
||||
databaseKeys.databaseFunctions(projectRef),
|
||||
({ signal }) => getDatabaseFunctions({ projectRef, connectionString }, signal),
|
||||
{
|
||||
select(data) {
|
||||
return (data as any)?.result ?? []
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { toast } from 'sonner'
|
||||
import { z } from 'zod'
|
||||
|
||||
import pgMeta from '@supabase/pg-meta'
|
||||
import { databaseKeys } from 'data/database/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import type { DatabaseFunction } from './database-functions-query'
|
||||
|
||||
@@ -50,7 +50,7 @@ export const useDatabaseFunctionUpdateMutation = ({
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await queryClient.invalidateQueries(sqlKeys.query(projectRef, ['functions-list']))
|
||||
await queryClient.invalidateQueries(databaseKeys.databaseFunctions(projectRef))
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import pgMeta from '@supabase/pg-meta'
|
||||
import { QueryClient, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { executeSql, ExecuteSqlError } from 'data/sql/execute-sql-query'
|
||||
import { databaseRoleKeys } from './keys'
|
||||
|
||||
export type DatabaseRolesVariables = {
|
||||
projectRef?: string
|
||||
@@ -14,28 +14,34 @@ export type PgRole = z.infer<typeof pgMeta.roles.zod>
|
||||
|
||||
const pgMetaRolesList = pgMeta.roles.list()
|
||||
|
||||
export async function getDatabaseRoles(
|
||||
{ projectRef, connectionString }: DatabaseRolesVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql: pgMetaRolesList.sql, queryKey: ['database-roles'] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result as PgRole[]
|
||||
}
|
||||
|
||||
export type DatabaseRolesData = z.infer<typeof pgMetaRolesList.zod>
|
||||
export type DatabaseRolesError = ExecuteSqlError
|
||||
|
||||
export const useDatabaseRolesQuery = <TData = DatabaseRolesData>(
|
||||
{ projectRef, connectionString }: DatabaseRolesVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, DatabaseRolesError, TData> = {}
|
||||
{ enabled = true, ...options }: UseQueryOptions<DatabaseRolesData, DatabaseRolesError, TData> = {}
|
||||
) =>
|
||||
useExecuteSqlQuery(
|
||||
useQuery<DatabaseRolesData, DatabaseRolesError, TData>(
|
||||
databaseRoleKeys.databaseRoles(projectRef),
|
||||
({ signal }) => getDatabaseRoles({ projectRef, connectionString }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: pgMetaRolesList.sql,
|
||||
queryKey: ['roles', 'list'],
|
||||
},
|
||||
{
|
||||
select(data) {
|
||||
return data.result
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
export function invalidateRolesQuery(client: QueryClient, projectRef: string | undefined) {
|
||||
return client.invalidateQueries(sqlKeys.query(projectRef, ['roles', 'list']))
|
||||
return client.invalidateQueries(databaseRoleKeys.databaseRoles(projectRef))
|
||||
}
|
||||
|
||||
4
apps/studio/data/database-roles/keys.ts
Normal file
4
apps/studio/data/database-roles/keys.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const databaseRoleKeys = {
|
||||
databaseRoles: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'database-roles'] as const,
|
||||
}
|
||||
@@ -42,7 +42,6 @@ export const useDatabaseHooksQuery = <TData = DatabaseTriggersData>(
|
||||
databaseTriggerKeys.list(projectRef),
|
||||
({ signal }) => getDatabaseTriggers({ projectRef, connectionString }, signal),
|
||||
{
|
||||
staleTime: 0,
|
||||
// @ts-ignore
|
||||
select(data) {
|
||||
return (data as PostgresTrigger[]).filter(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export const getDatabaseSizeQuery = () => {
|
||||
export const getDatabaseSizeSql = () => {
|
||||
const sql = /* SQL */ `
|
||||
select sum(pg_database_size(pg_database.datname))::bigint as db_size from pg_database;
|
||||
`.trim()
|
||||
@@ -14,23 +15,42 @@ export type DatabaseSizeVariables = {
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type DatabaseSizeData = { result: { db_size: number }[] }
|
||||
export type DatabaseSizeError = ExecuteSqlError
|
||||
|
||||
export const useDatabaseSizeQuery = <TData extends DatabaseSizeData = DatabaseSizeData>(
|
||||
export async function getDatabaseSize(
|
||||
{ projectRef, connectionString }: DatabaseSizeVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, DatabaseSizeError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getDatabaseSizeSql()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getDatabaseSizeQuery(),
|
||||
sql,
|
||||
queryKey: ['database-size'],
|
||||
},
|
||||
signal
|
||||
)
|
||||
|
||||
const dbSize = result?.[0]?.db_size
|
||||
if (typeof dbSize !== 'number') {
|
||||
throw new Error('Error fetching dbSize')
|
||||
}
|
||||
|
||||
return dbSize
|
||||
}
|
||||
|
||||
export type DatabaseSizeData = Awaited<ReturnType<typeof getDatabaseSize>>
|
||||
export type DatabaseSizeError = ExecuteSqlError
|
||||
|
||||
export const useDatabaseSizeQuery = <TData = DatabaseSizeData>(
|
||||
{ projectRef, connectionString }: DatabaseSizeVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<DatabaseSizeData, DatabaseSizeError, TData> = {}
|
||||
) =>
|
||||
useQuery<DatabaseSizeData, DatabaseSizeError, TData>(
|
||||
databaseKeys.databaseSize(projectRef),
|
||||
({ signal }) => getDatabaseSize({ projectRef, connectionString }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
staleTime: 1000 * 60, // default good for a minute
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { QueryClient, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import {
|
||||
executeSql,
|
||||
ExecuteSqlData,
|
||||
ExecuteSqlError,
|
||||
useExecuteSqlQuery,
|
||||
} from '../sql/execute-sql-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
type GetForeignKeyConstraintsVariables = {
|
||||
schema?: string
|
||||
@@ -42,7 +37,11 @@ export type ForeignKeyConstraint = {
|
||||
target_columns: string[]
|
||||
}
|
||||
|
||||
export const getForeignKeyConstraintsQuery = ({ schema }: GetForeignKeyConstraintsVariables) => {
|
||||
export const getForeignKeyConstraintsSql = ({ schema }: GetForeignKeyConstraintsVariables) => {
|
||||
if (!schema) {
|
||||
throw new Error('schema is required')
|
||||
}
|
||||
|
||||
const sql = /* SQL */ `
|
||||
SELECT
|
||||
con.oid as id,
|
||||
@@ -89,7 +88,7 @@ FROM
|
||||
INNER JOIN pg_namespace fnsp ON fnsp.oid = frel.relnamespace
|
||||
WHERE
|
||||
con.contype = 'f'
|
||||
${schema !== undefined ? `AND nsp.nspname = '${schema}'` : ''};
|
||||
AND nsp.nspname = '${schema}'
|
||||
`.trim()
|
||||
|
||||
return sql
|
||||
@@ -100,60 +99,55 @@ export type ForeignKeyConstraintsVariables = GetForeignKeyConstraintsVariables &
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type ForeignKeyConstraintsData = ForeignKeyConstraint[]
|
||||
export type ForeignKeyConstraintsError = ExecuteSqlError
|
||||
|
||||
function select(
|
||||
data:
|
||||
| {
|
||||
result: any
|
||||
}
|
||||
| undefined
|
||||
export async function getForeignKeyConstraints(
|
||||
{ projectRef, connectionString, schema }: ForeignKeyConstraintsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
return (data?.result ?? []).map((foreignKey: ForeignKeyConstraintRaw) => {
|
||||
const sql = getForeignKeyConstraintsSql({ schema })
|
||||
|
||||
const { result } = await executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql,
|
||||
queryKey: ['foreign-key-constraints', schema],
|
||||
},
|
||||
signal
|
||||
)
|
||||
|
||||
return (result ?? []).map((foreignKey: ForeignKeyConstraintRaw) => {
|
||||
return {
|
||||
...foreignKey,
|
||||
source_columns: foreignKey.source_columns.replace('{', '').replace('}', '').split(','),
|
||||
target_columns: foreignKey.target_columns.replace('{', '').replace('}', '').split(','),
|
||||
}
|
||||
})
|
||||
}) as ForeignKeyConstraint[]
|
||||
}
|
||||
|
||||
export const useForeignKeyConstraintsQuery = <
|
||||
TData extends ForeignKeyConstraintsData = ForeignKeyConstraintsData,
|
||||
>(
|
||||
export type ForeignKeyConstraintsData = Awaited<ReturnType<typeof getForeignKeyConstraints>>
|
||||
export type ForeignKeyConstraintsError = ExecuteSqlError
|
||||
|
||||
export const useForeignKeyConstraintsQuery = <TData = ForeignKeyConstraintsData>(
|
||||
{ projectRef, connectionString, schema }: ForeignKeyConstraintsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, ForeignKeyConstraintsError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<ForeignKeyConstraintsData, ForeignKeyConstraintsError, TData> = {}
|
||||
) =>
|
||||
useQuery<ForeignKeyConstraintsData, ForeignKeyConstraintsError, TData>(
|
||||
databaseKeys.foreignKeyConstraints(projectRef, schema),
|
||||
({ signal }) => getForeignKeyConstraints({ projectRef, connectionString, schema }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getForeignKeyConstraintsQuery({ schema }),
|
||||
queryKey: ['foreign-key-constraints', schema],
|
||||
},
|
||||
{
|
||||
select,
|
||||
enabled: enabled && typeof projectRef !== 'undefined' && typeof schema !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function prefetchForeignKeyConstraints(
|
||||
client: QueryClient,
|
||||
{ projectRef, connectionString, schema }: ForeignKeyConstraintsVariables
|
||||
) {
|
||||
return client
|
||||
.fetchQuery(sqlKeys.query(projectRef, ['foreign-key-constraints', schema]), ({ signal }) =>
|
||||
executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getForeignKeyConstraintsQuery({ schema }),
|
||||
queryKey: ['foreign-key-constraints', schema],
|
||||
},
|
||||
signal
|
||||
)
|
||||
)
|
||||
.then((data) => select(data) as ForeignKeyConstraintsData)
|
||||
return client.fetchQuery(databaseKeys.foreignKeyConstraints(projectRef, schema), ({ signal }) =>
|
||||
getForeignKeyConstraints({ projectRef, connectionString, schema }, signal)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
// [Joshen] Future refactor to move to pg-meta once available
|
||||
|
||||
export type DatabaseIndex = {
|
||||
name: string
|
||||
schema: string
|
||||
table: string
|
||||
definition: string
|
||||
enabled: boolean
|
||||
type GetIndexesArgs = {
|
||||
schema?: string
|
||||
}
|
||||
|
||||
export const getIndexesQuery = ({ schema }: { schema: string }) => {
|
||||
export const getIndexesSql = ({ schema }: GetIndexesArgs) => {
|
||||
const sql = /* SQL */ `
|
||||
SELECT schemaname as "schema",
|
||||
tablename as "table",
|
||||
@@ -24,29 +19,49 @@ WHERE schemaname = '${schema}';
|
||||
return sql
|
||||
}
|
||||
|
||||
type getIndexesDefinition = {
|
||||
export type DatabaseIndex = {
|
||||
name: string
|
||||
schema: string
|
||||
table: string
|
||||
definition: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type IndexesVariables = getIndexesDefinition & {
|
||||
export type IndexesVariables = GetIndexesArgs & {
|
||||
projectRef?: string
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type IndexesData = { result: DatabaseIndex[] }
|
||||
export async function getIndexes(
|
||||
{ schema, projectRef, connectionString }: IndexesVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!schema) {
|
||||
throw new Error('schema is required')
|
||||
}
|
||||
|
||||
const sql = getIndexesSql({ schema })
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['indexes', schema] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result as DatabaseIndex[]
|
||||
}
|
||||
|
||||
export type IndexesData = Awaited<ReturnType<typeof getIndexes>>
|
||||
export type IndexesError = ExecuteSqlError
|
||||
|
||||
export const useIndexesQuery = <TData extends IndexesData = IndexesData>(
|
||||
{ schema, projectRef, connectionString }: IndexesVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, IndexesError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
export const useIndexesQuery = <TData = IndexesData>(
|
||||
{ projectRef, connectionString, schema }: IndexesVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<IndexesData, IndexesError, TData> = {}
|
||||
) =>
|
||||
useQuery<IndexesData, IndexesError, TData>(
|
||||
databaseKeys.indexes(projectRef, schema),
|
||||
({ signal }) => getIndexes({ projectRef, connectionString, schema }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getIndexesQuery({ schema }),
|
||||
queryKey: ['indexes', schema],
|
||||
},
|
||||
options
|
||||
enabled: enabled && typeof projectRef !== 'undefined' && typeof schema !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { SupportedAssistantEntities } from 'components/ui/AIAssistantPanel/AIAssistant.types'
|
||||
|
||||
export const databaseKeys = {
|
||||
schemas: (projectRef: string | undefined) => ['projects', projectRef, 'schemas'] as const,
|
||||
indexes: (projectRef: string | undefined, schema: string | undefined) =>
|
||||
['projects', projectRef, 'indexes', schema] as const,
|
||||
keywords: (projectRef: string | undefined) => ['projects', projectRef, 'keywords'] as const,
|
||||
migrations: (projectRef: string | undefined) => ['projects', projectRef, 'migrations'] as const,
|
||||
tableColumns: (
|
||||
projectRef: string | undefined,
|
||||
schema: string | undefined,
|
||||
table: string | undefined
|
||||
) => ['projects', projectRef, 'table-columns', schema, table] as const,
|
||||
databaseFunctions: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'database-functions'] as const,
|
||||
entityDefinition: (projectRef: string | undefined, id?: number) =>
|
||||
['projects', projectRef, 'entity-definition', id] as const,
|
||||
entityDefinitions: (projectRef: string | undefined, schemas: string[]) =>
|
||||
@@ -9,13 +19,20 @@ export const databaseKeys = {
|
||||
['projects', projectRef, 'table-definition', id] as const,
|
||||
viewDefinition: (projectRef: string | undefined, id?: number) =>
|
||||
['projects', projectRef, 'view-definition', id] as const,
|
||||
backups: (projectRef: string | undefined) => [projectRef, 'database', 'backups'] as const,
|
||||
backups: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'database', 'backups'] as const,
|
||||
poolingConfiguration: (projectRef: string | undefined) =>
|
||||
[projectRef, 'database', 'pooling-configuration'] as const,
|
||||
['projects', projectRef, 'database', 'pooling-configuration'] as const,
|
||||
indexesFromQuery: (projectRef: string | undefined, query: string) =>
|
||||
['projects', projectRef, 'indexes', { query }] as const,
|
||||
indexAdvisorFromQuery: (projectRef: string | undefined, query: string) =>
|
||||
['projects', projectRef, 'index-advisor', { query }] as const,
|
||||
tableConstraints: (projectRef: string | undefined, id?: number) =>
|
||||
['projects', projectRef, 'table-constraints', id] as const,
|
||||
foreignKeyConstraints: (projectRef: string | undefined, schema?: string) =>
|
||||
['projects', projectRef, 'foreign-key-constraints', schema] as const,
|
||||
databaseSize: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'database-size'] as const,
|
||||
maxConnections: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'max-connections'] as const,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export type DatabaseKeyword = { word: string }
|
||||
|
||||
export const getKeywordsQuery = () => {
|
||||
export const getKeywordsSql = () => {
|
||||
const sql = /* SQL */ `
|
||||
SELECT word FROM pg_get_keywords();
|
||||
`.trim()
|
||||
@@ -16,27 +15,32 @@ export type KeywordsVariables = {
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type KeywordsData = { result: string[] }
|
||||
export async function getKeywords(
|
||||
{ projectRef, connectionString }: KeywordsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getKeywordsSql()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['keywords'] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result.map((x: { word: string }) => x.word.toLocaleLowerCase()) as string[]
|
||||
}
|
||||
|
||||
export type KeywordsData = Awaited<ReturnType<typeof getKeywords>>
|
||||
export type KeywordsError = ExecuteSqlError
|
||||
|
||||
export const useKeywordsQuery = <TData extends KeywordsData = KeywordsData>(
|
||||
export const useKeywordsQuery = <TData = KeywordsData>(
|
||||
{ projectRef, connectionString }: KeywordsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, KeywordsError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
{ enabled = true, ...options }: UseQueryOptions<KeywordsData, KeywordsError, TData> = {}
|
||||
) =>
|
||||
useQuery<KeywordsData, KeywordsError, TData>(
|
||||
databaseKeys.keywords(projectRef),
|
||||
({ signal }) => getKeywords({ projectRef, connectionString }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getKeywordsQuery(),
|
||||
queryKey: ['keywords'],
|
||||
},
|
||||
{
|
||||
select: (data) => {
|
||||
return {
|
||||
result: data.result.map((x: DatabaseKeyword) => x.word.toLocaleLowerCase()),
|
||||
} as any
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export const getMaxConnectionsQuery = () => {
|
||||
export const getMaxConnectionsSql = () => {
|
||||
const sql = /* SQL */ `show max_connections`
|
||||
|
||||
return sql
|
||||
@@ -14,26 +15,37 @@ export type MaxConnectionsVariables = {
|
||||
schema?: string
|
||||
}
|
||||
|
||||
export type MaxConnectionsData = { maxConnections: number }
|
||||
export async function getMaxConnections(
|
||||
{ projectRef, connectionString }: MaxConnectionsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getMaxConnectionsSql()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['max-connections'] },
|
||||
signal
|
||||
)
|
||||
|
||||
const connections = parseInt(result[0].max_connections)
|
||||
|
||||
return { maxConnections: connections }
|
||||
}
|
||||
|
||||
export type MaxConnectionsData = Awaited<ReturnType<typeof getMaxConnections>>
|
||||
export type MaxConnectionsError = ExecuteSqlError
|
||||
|
||||
export const useMaxConnectionsQuery = <TData extends MaxConnectionsData = MaxConnectionsData>(
|
||||
export const useMaxConnectionsQuery = <TData = MaxConnectionsData>(
|
||||
{ projectRef, connectionString }: MaxConnectionsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, MaxConnectionsError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery<TData>(
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<MaxConnectionsData, MaxConnectionsError, TData> = {}
|
||||
) =>
|
||||
useQuery<MaxConnectionsData, MaxConnectionsError, TData>(
|
||||
databaseKeys.maxConnections(projectRef),
|
||||
({ signal }) => getMaxConnections({ projectRef, connectionString }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getMaxConnectionsQuery(),
|
||||
queryKey: ['max-connections'],
|
||||
},
|
||||
{
|
||||
select: (data: { result: { max_connections: string }[] }) => {
|
||||
const connections = parseInt(data.result[0].max_connections)
|
||||
return { maxConnections: connections } as any
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export type DatabaseMigration = {
|
||||
version: string
|
||||
@@ -7,7 +8,7 @@ export type DatabaseMigration = {
|
||||
statements?: string[]
|
||||
}
|
||||
|
||||
export const getMigrationsQuery = () => {
|
||||
export const getMigrationsSql = () => {
|
||||
const sql = /* SQL */ `
|
||||
select
|
||||
*
|
||||
@@ -23,29 +24,44 @@ export type MigrationsVariables = {
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type MigrationsData = { result: DatabaseMigration[] }
|
||||
export async function getMigrations(
|
||||
{ projectRef, connectionString }: MigrationsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getMigrationsSql()
|
||||
|
||||
try {
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['migrations'] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result as DatabaseMigration[]
|
||||
} catch (error) {
|
||||
if (
|
||||
(error as ExecuteSqlError).message.includes(
|
||||
'relation "supabase_migrations.schema_migrations" does not exist'
|
||||
)
|
||||
) {
|
||||
return []
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export type MigrationsData = Awaited<ReturnType<typeof getMigrations>>
|
||||
export type MigrationsError = ExecuteSqlError
|
||||
|
||||
export const useMigrationsQuery = <TData extends MigrationsData = MigrationsData>(
|
||||
export const useMigrationsQuery = <TData = MigrationsData>(
|
||||
{ projectRef, connectionString }: MigrationsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, MigrationsError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
{ enabled = true, ...options }: UseQueryOptions<MigrationsData, MigrationsError, TData> = {}
|
||||
) =>
|
||||
useQuery<MigrationsData, MigrationsError, TData>(
|
||||
databaseKeys.migrations(projectRef),
|
||||
({ signal }) => getMigrations({ projectRef, connectionString }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getMigrationsQuery(),
|
||||
queryKey: ['migrations'],
|
||||
handleError: (error) => {
|
||||
if (
|
||||
error.message.includes('relation "supabase_migrations.schema_migrations" does not exist')
|
||||
) {
|
||||
return { result: [] }
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
options
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import pgMeta from '@supabase/pg-meta'
|
||||
import { QueryClient, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { z } from 'zod'
|
||||
|
||||
import {
|
||||
executeSql,
|
||||
ExecuteSqlData,
|
||||
ExecuteSqlError,
|
||||
useExecuteSqlQuery,
|
||||
} from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { executeSql, ExecuteSqlError } from 'data/sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export type SchemasVariables = {
|
||||
projectRef?: string
|
||||
@@ -22,42 +17,45 @@ const pgMetaSchemasList = pgMeta.schemas.list()
|
||||
export type SchemasData = z.infer<typeof pgMetaSchemasList.zod>
|
||||
export type SchemasError = ExecuteSqlError
|
||||
|
||||
export const useSchemasQuery = <TData = SchemasData>(
|
||||
export async function getSchemas(
|
||||
{ projectRef, connectionString }: SchemasVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, SchemasError, TData> = {}
|
||||
) =>
|
||||
useExecuteSqlQuery(
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const { result } = await executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: pgMetaSchemasList.sql,
|
||||
queryKey: ['schemas', 'list'],
|
||||
queryKey: ['schemas'],
|
||||
},
|
||||
signal
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const useSchemasQuery = <TData = SchemasData>(
|
||||
{ projectRef, connectionString }: SchemasVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<SchemasData, SchemasError, TData> = {}
|
||||
) =>
|
||||
useQuery<SchemasData, SchemasError, TData>(
|
||||
databaseKeys.schemas(projectRef),
|
||||
({ signal }) => getSchemas({ projectRef, connectionString }, signal),
|
||||
{
|
||||
select(data) {
|
||||
return data.result
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
export function invalidateSchemasQuery(client: QueryClient, projectRef: string | undefined) {
|
||||
return client.invalidateQueries(sqlKeys.query(projectRef, ['schemas', 'list']))
|
||||
return client.invalidateQueries(databaseKeys.schemas(projectRef))
|
||||
}
|
||||
|
||||
export function prefetchSchemas(
|
||||
client: QueryClient,
|
||||
{ projectRef, connectionString }: SchemasVariables
|
||||
) {
|
||||
return client.fetchQuery(sqlKeys.query(projectRef, ['schemas', 'list']), ({ signal }) =>
|
||||
executeSql(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: pgMetaSchemasList.sql,
|
||||
queryKey: ['schemas', 'list'],
|
||||
},
|
||||
signal
|
||||
)
|
||||
return client.fetchQuery(databaseKeys.schemas(projectRef), ({ signal }) =>
|
||||
getSchemas({ projectRef, connectionString }, signal)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export type TableColumn = {
|
||||
schemaname: string
|
||||
@@ -9,7 +10,7 @@ export type TableColumn = {
|
||||
columns: any[]
|
||||
}
|
||||
|
||||
export const getTableColumnsQuery = (table?: string, schema?: string) => {
|
||||
export const getTableColumnsSql = ({ table, schema }: { table?: string; schema?: string }) => {
|
||||
const conditions = []
|
||||
if (table) {
|
||||
conditions.push(`tablename = '${table}'`)
|
||||
@@ -91,20 +92,32 @@ export type TableColumnsVariables = {
|
||||
schema?: string
|
||||
}
|
||||
|
||||
export type TableColumnsData = { result: TableColumn[] }
|
||||
export async function getTableColumns(
|
||||
{ projectRef, connectionString, table, schema }: TableColumnsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getTableColumnsSql({ table, schema })
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['table-columns', schema, table] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result as TableColumn[]
|
||||
}
|
||||
|
||||
export type TableColumnsData = Awaited<ReturnType<typeof getTableColumns>>
|
||||
export type TableColumnsError = ExecuteSqlError
|
||||
|
||||
export const useTableColumnsQuery = <TData extends TableColumnsData = TableColumnsData>(
|
||||
{ projectRef, connectionString, table, schema }: TableColumnsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, TableColumnsError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
export const useTableColumnsQuery = <TData = TableColumnsData>(
|
||||
{ projectRef, connectionString, schema, table }: TableColumnsVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<TableColumnsData, TableColumnsError, TData> = {}
|
||||
) =>
|
||||
useQuery<TableColumnsData, TableColumnsError, TData>(
|
||||
databaseKeys.tableColumns(projectRef, schema, table),
|
||||
({ signal }) => getTableColumns({ projectRef, connectionString, schema, table }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getTableColumnsQuery(table, schema),
|
||||
queryKey: ['table-columns', schema, table],
|
||||
},
|
||||
options
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export async function getEntityTypes(
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql,
|
||||
queryKey: ['public', 'entity-types'],
|
||||
queryKey: ['entity-types', ...schemas, page],
|
||||
},
|
||||
signal
|
||||
)
|
||||
@@ -149,7 +149,6 @@ export const useEntityTypesQuery = <TData = EntityTypesData>(
|
||||
),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
staleTime: 0,
|
||||
getNextPageParam(lastPage, pages) {
|
||||
const page = pages.length
|
||||
const currentTotalCount = page * limit
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function getEnumeratedTypes(
|
||||
if (connectionString) headers.set('x-connection-encrypted', connectionString)
|
||||
|
||||
const { data, error } = await get('/platform/pg-meta/{ref}/types', {
|
||||
// @ts-ignore: We don't need to pass included included_schemas / excluded_schemas in query params
|
||||
// @ts-expect-error: We don't need to pass included included_schemas / excluded_schemas in query params
|
||||
params: {
|
||||
header: { 'x-connection-encrypted': connectionString! },
|
||||
path: { ref: projectRef },
|
||||
@@ -50,7 +50,6 @@ export const useEnumeratedTypesQuery = <TData = EnumeratedTypesData>(
|
||||
({ signal }) => getEnumeratedTypes({ projectRef, connectionString }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
staleTime: 0,
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -6,12 +6,13 @@ import {
|
||||
WrapperMeta,
|
||||
} from 'components/interfaces/Database/Wrappers/Wrappers.types'
|
||||
import { entityTypeKeys } from 'data/entity-types/keys'
|
||||
import { foreignTableKeys } from 'data/foreign-tables/keys'
|
||||
import { pgSodiumKeys } from 'data/pg-sodium-keys/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { wrapWithTransaction } from 'data/sql/utils/transaction'
|
||||
import { vaultSecretsKeys } from 'data/vault/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { fdwKeys } from './keys'
|
||||
|
||||
export type FDWCreateVariables = {
|
||||
projectRef?: string
|
||||
@@ -162,10 +163,11 @@ export const useFDWCreateMutation = ({
|
||||
const { projectRef } = variables
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(fdwKeys.list(projectRef), { refetchType: 'all' }),
|
||||
queryClient.invalidateQueries(entityTypeKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, ['fdws']), { refetchType: 'all' }),
|
||||
queryClient.invalidateQueries(foreignTableKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(pgSodiumKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, vaultSecretsKeys.list(projectRef))),
|
||||
queryClient.invalidateQueries(vaultSecretsKeys.list(projectRef)),
|
||||
])
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -3,13 +3,14 @@ import { toast } from 'sonner'
|
||||
|
||||
import type { WrapperMeta } from 'components/interfaces/Database/Wrappers/Wrappers.types'
|
||||
import { entityTypeKeys } from 'data/entity-types/keys'
|
||||
import { foreignTableKeys } from 'data/foreign-tables/keys'
|
||||
import { pgSodiumKeys } from 'data/pg-sodium-keys/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { wrapWithTransaction } from 'data/sql/utils/transaction'
|
||||
import { vaultSecretsKeys } from 'data/vault/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { FDW } from './fdws-query'
|
||||
import { fdwKeys } from './keys'
|
||||
|
||||
export type FDWDeleteVariables = {
|
||||
projectRef: string
|
||||
@@ -73,10 +74,11 @@ export const useFDWDeleteMutation = ({
|
||||
const { projectRef } = variables
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(fdwKeys.list(projectRef), { refetchType: 'all' }),
|
||||
queryClient.invalidateQueries(entityTypeKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, ['fdws'])),
|
||||
queryClient.invalidateQueries(foreignTableKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(pgSodiumKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, vaultSecretsKeys.list(projectRef))),
|
||||
queryClient.invalidateQueries(vaultSecretsKeys.list(projectRef)),
|
||||
])
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -3,15 +3,16 @@ import { toast } from 'sonner'
|
||||
|
||||
import type { WrapperMeta } from 'components/interfaces/Database/Wrappers/Wrappers.types'
|
||||
import { entityTypeKeys } from 'data/entity-types/keys'
|
||||
import { foreignTableKeys } from 'data/foreign-tables/keys'
|
||||
import { pgSodiumKeys } from 'data/pg-sodium-keys/keys'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { wrapWithTransaction } from 'data/sql/utils/transaction'
|
||||
import { vaultSecretsKeys } from 'data/vault/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { getCreateFDWSql } from './fdw-create-mutation'
|
||||
import { getDeleteFDWSql } from './fdw-delete-mutation'
|
||||
import { FDW } from './fdws-query'
|
||||
import { fdwKeys } from './keys'
|
||||
|
||||
export type FDWUpdateVariables = {
|
||||
projectRef?: string
|
||||
@@ -72,10 +73,11 @@ export const useFDWUpdateMutation = ({
|
||||
const { projectRef } = variables
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries(fdwKeys.list(projectRef), { refetchType: 'all' }),
|
||||
queryClient.invalidateQueries(entityTypeKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, ['fdws'])),
|
||||
queryClient.invalidateQueries(foreignTableKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(pgSodiumKeys.list(projectRef)),
|
||||
queryClient.invalidateQueries(sqlKeys.query(projectRef, vaultSecretsKeys.list(projectRef))),
|
||||
queryClient.invalidateQueries(vaultSecretsKeys.list(projectRef)),
|
||||
])
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { fdwKeys } from './keys'
|
||||
|
||||
export const getFDWsSql = () => {
|
||||
const sql = /* SQL */ `
|
||||
@@ -62,28 +63,37 @@ export type FDW = {
|
||||
tables: FDWTable[]
|
||||
}
|
||||
|
||||
export type FDWsResponse = {
|
||||
result: FDW[]
|
||||
}
|
||||
|
||||
export type FDWsVariables = {
|
||||
projectRef?: string
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type FDWsData = FDWsResponse
|
||||
export async function getFDWs(
|
||||
{ projectRef, connectionString }: FDWsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getFDWsSql()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['fdws'] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result as FDW[]
|
||||
}
|
||||
|
||||
export type FDWsData = Awaited<ReturnType<typeof getFDWs>>
|
||||
export type FDWsError = ExecuteSqlError
|
||||
|
||||
export const useFDWsQuery = <TData extends FDWsData = FDWsData>(
|
||||
export const useFDWsQuery = <TData = FDWsData>(
|
||||
{ projectRef, connectionString }: FDWsVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, FDWsError, TData> = {}
|
||||
{ enabled = true, ...options }: UseQueryOptions<FDWsData, FDWsError, TData> = {}
|
||||
) =>
|
||||
useExecuteSqlQuery(
|
||||
useQuery<FDWsData, FDWsError, TData>(
|
||||
fdwKeys.list(projectRef),
|
||||
({ signal }) => getFDWs({ projectRef, connectionString }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getFDWsSql(),
|
||||
queryKey: ['fdws'],
|
||||
},
|
||||
options
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
3
apps/studio/data/fdw/keys.ts
Normal file
3
apps/studio/data/fdw/keys.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const fdwKeys = {
|
||||
list: (projectRef: string | undefined) => ['projects', projectRef, 'fdws'] as const,
|
||||
}
|
||||
@@ -49,7 +49,6 @@ export const useForeignTablesQuery = <TData = ForeignTablesData>(
|
||||
({ signal }) => getForeignTables({ projectRef, connectionString, schema }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
staleTime: 0,
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -54,6 +54,8 @@ export const useMaterializedViewsQuery = <TData = MaterializedViewsData>(
|
||||
({ signal }) => getMaterializedViews({ projectRef, connectionString, schema }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
// We're using a staleTime of 0 here because the only way to create a
|
||||
// materialized view is via SQL, which we don't know about
|
||||
staleTime: 0,
|
||||
...options,
|
||||
}
|
||||
|
||||
@@ -4,4 +4,6 @@ export const replicaKeys = {
|
||||
['project', projectRef, 'replicas-statuses'] as const,
|
||||
loadBalancers: (projectRef: string | undefined) =>
|
||||
['project', projectRef, 'load-balancers'] as const,
|
||||
replicaLag: (projectRef: string | undefined, id: string) =>
|
||||
['project', projectRef, 'replica-lag', id] as const,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { replicaKeys } from './keys'
|
||||
|
||||
export const replicationLagQuery = () => {
|
||||
export const replicationLagSql = () => {
|
||||
const sql = /* SQL */ `
|
||||
select
|
||||
case
|
||||
@@ -20,24 +21,34 @@ export type ReplicationLagVariables = {
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type ReplicationLagData = number
|
||||
export async function getReplicationLag(
|
||||
{ projectRef, connectionString, id }: ReplicationLagVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = replicationLagSql()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['replica-lag', id] },
|
||||
signal
|
||||
)
|
||||
|
||||
return Number((result[0] ?? null)?.physical_replica_lag_second ?? 0)
|
||||
}
|
||||
|
||||
export type ReplicationLagData = Awaited<ReturnType<typeof getReplicationLag>>
|
||||
export type ReplicationLagError = ExecuteSqlError
|
||||
|
||||
export const useReplicationLagQuery = <TData extends ReplicationLagData = ReplicationLagData>(
|
||||
export const useReplicationLagQuery = <TData = ReplicationLagData>(
|
||||
{ projectRef, connectionString, id }: ReplicationLagVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<ExecuteSqlData, ReplicationLagError, TData> = {}
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<ReplicationLagData, ReplicationLagError, TData> = {}
|
||||
) =>
|
||||
useExecuteSqlQuery(
|
||||
useQuery<ReplicationLagData, ReplicationLagError, TData>(
|
||||
replicaKeys.replicaLag(projectRef, id),
|
||||
({ signal }) => getReplicationLag({ projectRef, connectionString, id }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: replicationLagQuery(),
|
||||
queryKey: ['replica-lag', id],
|
||||
},
|
||||
{
|
||||
select(data) {
|
||||
return Number((data.result[0] ?? null)?.physical_replica_lag_second ?? 0) as TData
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined' && typeof id !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const useQueryAbortMutation = ({
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await queryClient.invalidateQueries(sqlKeys.query(projectRef, ['ongoing-queries']))
|
||||
await queryClient.invalidateQueries(sqlKeys.ongoingQueries(projectRef))
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
|
||||
@@ -55,7 +55,7 @@ export async function executeSql(
|
||||
params: {
|
||||
header: { 'x-connection-encrypted': connectionString ?? '' },
|
||||
path: { ref: projectRef },
|
||||
// @ts-ignore: This is just a client side thing to identify queries better
|
||||
// @ts-expect-error: This is just a client side thing to identify queries better
|
||||
query: {
|
||||
key:
|
||||
queryKey?.filter((seg) => typeof seg === 'string' || typeof seg === 'number').join('-') ??
|
||||
@@ -64,7 +64,7 @@ export async function executeSql(
|
||||
},
|
||||
body: { query: sql },
|
||||
headers: Object.fromEntries(headers),
|
||||
} as any) // Needed to fix generated api types for now
|
||||
})
|
||||
|
||||
if (error) {
|
||||
if (
|
||||
@@ -114,6 +114,9 @@ export async function executeSql(
|
||||
export type ExecuteSqlData = Awaited<ReturnType<typeof executeSql>>
|
||||
export type ExecuteSqlError = ResponseError
|
||||
|
||||
/**
|
||||
* @deprecated Use the regular useQuery with a function that calls executeSql() instead
|
||||
*/
|
||||
export const useExecuteSqlQuery = <TData = ExecuteSqlData>(
|
||||
{
|
||||
projectRef,
|
||||
|
||||
@@ -3,4 +3,6 @@ import type { QueryKey } from '@tanstack/react-query'
|
||||
export const sqlKeys = {
|
||||
query: (projectRef: string | undefined, queryKey: QueryKey) =>
|
||||
['projects', projectRef, 'query', ...queryKey] as const,
|
||||
ongoingQueries: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'ongoing-queries'] as const,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { sqlKeys } from './keys'
|
||||
|
||||
type OngoingQuery = {
|
||||
pid: number
|
||||
@@ -7,7 +8,7 @@ type OngoingQuery = {
|
||||
query_start: string
|
||||
}
|
||||
|
||||
export const getOngoingQueries = () => {
|
||||
export const getOngoingQueriesSql = () => {
|
||||
const sql = /* SQL */ `
|
||||
select pid, query, query_start from pg_stat_activity where state = 'active' and datname = 'postgres';
|
||||
`.trim()
|
||||
@@ -20,27 +21,35 @@ export type OngoingQueriesVariables = {
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type OngoingQueriesData = OngoingQuery[]
|
||||
export async function getOngoingQueries(
|
||||
{ projectRef, connectionString }: OngoingQueriesVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getOngoingQueriesSql().trim()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['ongoing-queries'] },
|
||||
signal
|
||||
)
|
||||
|
||||
return (result ?? []).filter((x: OngoingQuery) => !x.query.startsWith(sql)) as OngoingQuery[]
|
||||
}
|
||||
|
||||
export type OngoingQueriesData = Awaited<ReturnType<typeof getOngoingQueries>>
|
||||
export type OngoingQueriesError = ExecuteSqlError
|
||||
|
||||
export const useOngoingQueriesQuery = <TData extends OngoingQueriesData = OngoingQueriesData>(
|
||||
export const useOngoingQueriesQuery = <TData = OngoingQueriesData>(
|
||||
{ projectRef, connectionString }: OngoingQueriesVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, OngoingQueriesError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getOngoingQueries(),
|
||||
queryKey: ['ongoing-queries'],
|
||||
},
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<OngoingQueriesData, OngoingQueriesError, TData> = {}
|
||||
) =>
|
||||
useQuery<OngoingQueriesData, OngoingQueriesError, TData>(
|
||||
sqlKeys.ongoingQueries(projectRef),
|
||||
({ signal }) => getOngoingQueries({ projectRef, connectionString }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
select(data) {
|
||||
return (data?.result ?? []).filter(
|
||||
(x: OngoingQuery) => !x.query.startsWith(getOngoingQueries())
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ type TableRowKeyArgs = Omit<GetTableRowsArgs, 'table'> & { table?: { id?: number
|
||||
|
||||
export const tableRowKeys = {
|
||||
tableRows: (projectRef?: string, { table, ...args }: TableRowKeyArgs = {}) =>
|
||||
[projectRef, 'table-rows', table?.id, 'rows', args] as const,
|
||||
['projects', projectRef, 'table-rows', table?.id, 'rows', args] as const,
|
||||
tableRowsCount: (projectRef?: string, { table, ...args }: TableRowKeyArgs = {}) =>
|
||||
[projectRef, 'table-rows', table?.id, 'count', args] as const,
|
||||
['projects', projectRef, 'table-rows', table?.id, 'count', args] as const,
|
||||
tableRowsAndCount: (projectRef?: string, tableId?: number) =>
|
||||
[projectRef, 'table-rows', tableId] as const,
|
||||
['projects', projectRef, 'table-rows', tableId] as const,
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export const useTablesQuery = <TData = TablesData>(
|
||||
return useQuery<TablesData, TablesError, TData>(
|
||||
tableKeys.list(projectRef, schema, includeColumns),
|
||||
({ signal }) => getTables({ projectRef, connectionString, schema, includeColumns }, signal),
|
||||
{ enabled: enabled && typeof projectRef !== 'undefined', staleTime: 0, ...options }
|
||||
{ enabled: enabled && typeof projectRef !== 'undefined', ...options }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { toast } from 'sonner'
|
||||
|
||||
import { Query } from 'components/grid/query/Query'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError, VaultSecret } from 'types'
|
||||
import { vaultSecretsKeys } from './keys'
|
||||
|
||||
@@ -44,9 +43,7 @@ export const useVaultSecretCreateMutation = ({
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await queryClient.invalidateQueries(
|
||||
sqlKeys.query(projectRef, vaultSecretsKeys.list(projectRef))
|
||||
)
|
||||
await queryClient.invalidateQueries(vaultSecretsKeys.list(projectRef))
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { toast } from 'sonner'
|
||||
|
||||
import { Query } from 'components/grid/query/Query'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError } from 'types'
|
||||
import { vaultSecretsKeys } from './keys'
|
||||
|
||||
@@ -40,9 +39,7 @@ export const useVaultSecretDeleteMutation = ({
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
await queryClient.invalidateQueries(
|
||||
sqlKeys.query(projectRef, vaultSecretsKeys.list(projectRef))
|
||||
)
|
||||
await queryClient.invalidateQueries(vaultSecretsKeys.list(projectRef))
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { toast } from 'sonner'
|
||||
|
||||
import { Query } from 'components/grid/query/Query'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import type { ResponseError, VaultSecret } from 'types'
|
||||
import { vaultSecretsKeys } from './keys'
|
||||
|
||||
@@ -48,9 +47,7 @@ export const useVaultSecretUpdateMutation = ({
|
||||
const { id, projectRef } = variables
|
||||
await Promise.all([
|
||||
queryClient.removeQueries(vaultSecretsKeys.getDecryptedValue(projectRef, id)),
|
||||
queryClient.invalidateQueries(
|
||||
sqlKeys.query(projectRef, vaultSecretsKeys.list(projectRef))
|
||||
),
|
||||
queryClient.invalidateQueries(vaultSecretsKeys.list(projectRef)),
|
||||
])
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { Query } from 'components/grid/query/Query'
|
||||
import type { VaultSecret } from 'types'
|
||||
import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
|
||||
import { vaultSecretsKeys } from './keys'
|
||||
|
||||
export const getVaultSecretsQuery = () => {
|
||||
export const getVaultSecretsSql = () => {
|
||||
const sql = new Query()
|
||||
.from('secrets', 'vault')
|
||||
.select('id,name,description,secret,key_id,created_at,updated_at')
|
||||
@@ -18,26 +18,32 @@ export type VaultSecretsVariables = {
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type VaultSecretsData = VaultSecret[]
|
||||
export async function getVaultSecrets(
|
||||
{ projectRef, connectionString }: VaultSecretsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const sql = getVaultSecretsSql()
|
||||
|
||||
const { result } = await executeSql(
|
||||
{ projectRef, connectionString, sql, queryKey: ['vault-secrets'] },
|
||||
signal
|
||||
)
|
||||
|
||||
return result as VaultSecret[]
|
||||
}
|
||||
|
||||
export type VaultSecretsData = Awaited<ReturnType<typeof getVaultSecrets>>
|
||||
export type VaultSecretsError = ExecuteSqlError
|
||||
|
||||
export const useVaultSecretsQuery = <TData extends VaultSecretsData = VaultSecretsData>(
|
||||
export const useVaultSecretsQuery = <TData = VaultSecretsData>(
|
||||
{ projectRef, connectionString }: VaultSecretsVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<ExecuteSqlData, VaultSecretsError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
{ enabled = true, ...options }: UseQueryOptions<VaultSecretsData, VaultSecretsError, TData> = {}
|
||||
) =>
|
||||
useQuery<VaultSecretsData, VaultSecretsError, TData>(
|
||||
vaultSecretsKeys.list(projectRef),
|
||||
({ signal }) => getVaultSecrets({ projectRef, connectionString }, signal),
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getVaultSecretsQuery(),
|
||||
queryKey: vaultSecretsKeys.list(projectRef),
|
||||
},
|
||||
{
|
||||
select(data) {
|
||||
return data.result
|
||||
},
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ export const useViewsQuery = <TData = ViewsData>(
|
||||
({ signal }) => getViews({ projectRef, connectionString, schema }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
// We're using a staleTime of 0 here because the only way to create a
|
||||
// view is via SQL, which we don't know about
|
||||
staleTime: 0,
|
||||
...options,
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ import { useProjectDiskResizeMutation } from 'data/config/project-disk-resize-mu
|
||||
import { useDatabaseSizeQuery } from 'data/database/database-size-query'
|
||||
import { useDatabaseReport } from 'data/reports/database-report-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { TIME_PERIODS_INFRA } from 'lib/constants/metrics'
|
||||
import { formatBytes } from 'lib/helpers'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
|
||||
const DatabaseReport: NextPageWithLayout = () => {
|
||||
return (
|
||||
@@ -59,7 +59,7 @@ const DatabaseUsage = () => {
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const databaseSizeBytes = data?.result[0].db_size ?? 0
|
||||
const databaseSizeBytes = data ?? 0
|
||||
const currentDiskSize = project?.volumeSizeGb ?? 0
|
||||
|
||||
const [showIncreaseDiskSizeModal, setshowIncreaseDiskSizeModal] = useState(false)
|
||||
|
||||
@@ -112,9 +112,9 @@ const SqlEditor: NextPageWithLayout = () => {
|
||||
if (pgInfoRef.current === null) {
|
||||
pgInfoRef.current = {}
|
||||
}
|
||||
pgInfoRef.current.tableColumns = tableColumns?.result
|
||||
pgInfoRef.current.tableColumns = tableColumns
|
||||
pgInfoRef.current.schemas = schemas
|
||||
pgInfoRef.current.keywords = keywords?.result
|
||||
pgInfoRef.current.keywords = keywords
|
||||
pgInfoRef.current.functions = functions
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const dismissToast = async (page: Page) => {
|
||||
test.describe('Table Editor page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const tableResponsePromise = page.waitForResponse(
|
||||
'http://localhost:8082/api/pg-meta/default/query?key=public-entity-types',
|
||||
'http://localhost:8082/api/pg-meta/default/query?key=entity-types-public-0',
|
||||
{ timeout: 0 }
|
||||
)
|
||||
await page.goto('/project/default/editor')
|
||||
@@ -108,7 +108,7 @@ test.describe('Table Editor page', () => {
|
||||
|
||||
test('should check the auth schema', async ({ page }) => {
|
||||
const tableResponsePromise = page.waitForResponse(
|
||||
'http://localhost:8082/api/pg-meta/default/query?key=public-entity-types',
|
||||
'http://localhost:8082/api/pg-meta/default/query?key=entity-types-public-0',
|
||||
{ timeout: 0 }
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user