-
+
{table.rls_enabled ? (
@@ -53,7 +57,7 @@ const PolicyTableRowHeader = ({
)}
{table.name}
-
+
{isTableLocked && (
diff --git a/apps/studio/components/interfaces/Database/Tables/TableList.tsx b/apps/studio/components/interfaces/Database/Tables/TableList.tsx
index 1242353bfee..519347c7858 100644
--- a/apps/studio/components/interfaces/Database/Tables/TableList.tsx
+++ b/apps/studio/components/interfaces/Database/Tables/TableList.tsx
@@ -30,12 +30,12 @@ import { useDatabasePublicationsQuery } from 'data/database-publications/databas
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import { useForeignTablesQuery } from 'data/foreign-tables/foreign-tables-query'
import { useMaterializedViewsQuery } from 'data/materialized-views/materialized-views-query'
+import { usePrefetchEditorTablePage } from 'data/prefetchers/project.$ref.editor.$id'
import { useTablesQuery } from 'data/tables/tables-query'
import { useViewsQuery } from 'data/views/views-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
-import { useTableEditorStateSnapshot } from 'state/table-editor'
import {
Button,
Checkbox_Shadcn_,
@@ -74,6 +74,8 @@ const TableList = ({
const { ref } = useParams()
const { project } = useProjectContext()
+ const prefetchEditorTablePage = usePrefetchEditorTablePage()
+
const { selectedSchema, setSelectedSchema } = useQuerySchemaState()
const [filterString, setFilterString] = useState('')
@@ -476,6 +478,9 @@ const TableList = ({
onClick={() =>
router.push(`/project/${project?.ref}/editor/${x.id}`)
}
+ onMouseEnter={() =>
+ prefetchEditorTablePage({ id: x.id ? String(x.id) : undefined })
+ }
>
View in Table Editor
diff --git a/apps/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx b/apps/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx
index ffdb8ae54d9..6dc505446c8 100644
--- a/apps/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx
+++ b/apps/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx
@@ -7,6 +7,7 @@ import Link from 'next/link'
import { useParams } from 'common'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import type { FDW } from 'data/fdw/fdws-query'
+import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Collapsible } from 'ui'
import type { WrapperMeta } from './Wrappers.types'
@@ -119,11 +120,15 @@ const WrapperRow = ({
{wrapper.tables ? (
wrapper.tables.map((table: any) => (
-
+
{table.name}
-
+
))
) : (
No tables available
diff --git a/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx b/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx
index 19b5708425e..e77fbe11591 100644
--- a/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx
+++ b/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx
@@ -1,8 +1,9 @@
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
-import { useRouter } from 'next/router'
+import { useParams } from 'common'
import Panel from 'components/ui/Panel'
+import { EditorIndexPageLink } from 'data/prefetchers/project.$ref.editor'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { Auth, EdgeFunctions, Realtime, SqlEditor, Storage, TableEditor } from 'icons'
import { Button } from 'ui'
@@ -10,8 +11,7 @@ import APIKeys from './APIKeys'
import GetStartedHero from './GetStartedHero'
const NewProjectPanel = () => {
- const router = useRouter()
- const { ref } = router.query
+ const { ref } = useParams()
const {
projectAuthAll: authEnabled,
@@ -47,7 +47,7 @@ const NewProjectPanel = () => {
}>
- Table Editor
+ Table Editor
}>
SQL editor
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx
index 76f0ec80047..13f03b53110 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx
@@ -13,13 +13,13 @@ import RefreshButton from 'components/grid/components/header/RefreshButton'
import FilterPopover from 'components/grid/components/header/filter/FilterPopover'
import { SortPopover } from 'components/grid/components/header/sort'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import { useEncryptedColumnsQuery } from 'data/encrypted-columns/encrypted-columns-query'
import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { useTableQuery } from 'data/tables/table-query'
import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state'
import { SidePanel } from 'ui'
import ActionBar from '../../ActionBar'
import { ForeignKey } from '../../ForeignKeySelector/ForeignKeySelector.types'
-import { useEncryptedColumns } from '../../SidePanelEditor.utils'
import Pagination from './Pagination'
import SelectorGrid from './SelectorGrid'
@@ -47,7 +47,12 @@ const ForeignRowSelector = ({
id: tableId,
})
- const encryptedColumns = useEncryptedColumns({ schemaName, tableName })
+ const { data: encryptedColumns } = useEncryptedColumnsQuery({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ schema: schemaName,
+ tableName,
+ })
const supaTable =
table &&
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx
index 3f8035af672..37c28bcd263 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx
@@ -1,11 +1,9 @@
import type { PostgresPrimaryKey, PostgresTable } from '@supabase/postgres-meta'
import { chunk, find, isEmpty, isEqual } from 'lodash'
import Papa from 'papaparse'
-import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { Query } from 'components/grid/query/Query'
-import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import SparkBar from 'components/ui/SparkBar'
import { createDatabaseColumn } from 'data/database-columns/database-column-create-mutation'
import { deleteDatabaseColumn } from 'data/database-columns/database-column-delete-mutation'
@@ -23,7 +21,6 @@ import { deleteTable as deleteTableMutation } from 'data/tables/table-delete-mut
import { getTable } from 'data/tables/table-query'
import { updateTable as updateTableMutation } from 'data/tables/table-update-mutation'
import { getTables } from 'data/tables/tables-query'
-import { getViews } from 'data/views/views-query'
import { timeout, tryParseJson } from 'lib/helpers'
import {
generateCreateColumnPayload,
@@ -37,67 +34,6 @@ import type { ImportContent } from './TableEditor/TableEditor.types'
const BATCH_SIZE = 1000
const CHUNK_SIZE = 1024 * 1024 * 0.1 // 0.1MB
-export interface UseEncryptedColumnsArgs {
- schemaName?: string
- tableName?: string
-}
-const listEncryptedColumns = async (
- projectRef: string,
- connectionString: string | undefined = undefined,
- schema: string,
- table: string
-) => {
- if (!table) return []
-
- const views = await getViews({ projectRef, connectionString, schema })
- const decryptedView = views.find((view) => view.name === `decrypted_${table}`)
- if (!decryptedView) return []
-
- try {
- const encryptedColumns = await executeSql({
- projectRef,
- connectionString,
- sql: `SELECT column_name as name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'decrypted_${table}' and column_name like 'decrypted_%'`,
- })
- return encryptedColumns.result.map((column: any) => column.name.split('decrypted_')[1])
- } catch (error) {
- console.error('Error fetching encrypted columns', error)
- return []
- }
-}
-
-export function useEncryptedColumns({ schemaName, tableName }: UseEncryptedColumnsArgs) {
- const { project } = useProjectContext()
- const [encryptedColumns, setEncryptedColumns] = useState([])
-
- useEffect(() => {
- let isMounted = true
-
- const getEncryptedColumns = async () => {
- if (schemaName !== undefined && tableName !== undefined) {
- const columns = await listEncryptedColumns(
- project?.ref!,
- project?.connectionString,
- schemaName,
- tableName
- )
-
- if (isMounted) {
- setEncryptedColumns(columns)
- }
- }
- }
-
- getEncryptedColumns()
-
- return () => {
- isMounted = false
- }
- }, [schemaName, tableName])
-
- return encryptedColumns
-}
-
/**
* The functions below are basically just queries but may be supported directly
* from the pg-meta library in the future
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ForeignKeysManagement/ForeignKeyRow.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ForeignKeysManagement/ForeignKeyRow.tsx
index 82d0142d26a..d74b6296931 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ForeignKeysManagement/ForeignKeyRow.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ForeignKeysManagement/ForeignKeyRow.tsx
@@ -1,10 +1,10 @@
import clsx from 'clsx'
import { useParams } from 'common'
-import Link from 'next/link'
+import { ArrowRight } from 'lucide-react'
import SVG from 'react-inlinesvg'
import { Badge, Button, cn } from 'ui'
-import { ArrowRight } from 'lucide-react'
+import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id'
import { BASE_PATH } from 'lib/constants'
import type { ForeignKey } from '../../ForeignKeySelector/ForeignKeySelector.types'
@@ -77,12 +77,13 @@ export const ForeignKeyRow = ({
/>
}
>
- closePanel()}
>
{foreignKey.schema}.{foreignKey.table}
-
+
diff --git a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
index 431ee1b7d5b..693f25789e5 100644
--- a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
@@ -1,4 +1,4 @@
-import type { PostgresColumn, PostgresRelationship, PostgresTable } from '@supabase/postgres-meta'
+import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { QueryKey, useQueryClient } from '@tanstack/react-query'
import { find, isUndefined } from 'lodash'
@@ -8,14 +8,11 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import { SupabaseGrid } from 'components/grid/SupabaseGrid'
-import { parseSupaTable } from 'components/grid/SupabaseGrid.utils'
+import { getSupaTable } from 'components/grid/SupabaseGrid.utils'
import { SupaTable } from 'components/grid/types'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
-import { FOREIGN_KEY_CASCADE_ACTION } from 'data/database/database-query-constants'
-import {
- ForeignKeyConstraint,
- useForeignKeyConstraintsQuery,
-} from 'data/database/foreign-key-constraints-query'
+import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
+import { useEncryptedColumnsQuery } from 'data/encrypted-columns/encrypted-columns-query'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import { sqlKeys } from 'data/sql/keys'
import { useTableRowUpdateMutation } from 'data/table-rows/table-row-update-mutation'
@@ -28,13 +25,12 @@ import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
import { EMPTY_ARR } from 'lib/void'
import { useGetImpersonatedRole } from 'state/role-impersonation-state'
import { useTableEditorStateSnapshot } from 'state/table-editor'
-import type { Dictionary, SchemaView } from 'types'
+import type { Dictionary } from 'types'
import { Button } from 'ui'
import GridHeaderActions from './GridHeaderActions'
import { TableGridSkeletonLoader } from './LoadingState'
import NotFoundState from './NotFoundState'
import SidePanelEditor from './SidePanelEditor/SidePanelEditor'
-import { useEncryptedColumns } from './SidePanelEditor/SidePanelEditor.utils'
import TableDefinition from './TableDefinition'
export interface TableGridEditorProps {
@@ -62,8 +58,10 @@ const TableGridEditor = ({
const canEditColumns = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'columns')
const isReadOnly = !canEditTables && !canEditColumns
- const encryptedColumns = useEncryptedColumns({
- schemaName: selectedTable?.schema,
+ const { data: encryptedColumns } = useEncryptedColumnsQuery({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ schema: selectedTable?.schema,
tableName: selectedTable?.name,
})
@@ -148,43 +146,16 @@ const TableGridEditor = ({
const isViewSelected =
entityType?.type === ENTITY_TYPE.VIEW || entityType?.type === ENTITY_TYPE.MATERIALIZED_VIEW
const isTableSelected = entityType?.type === ENTITY_TYPE.TABLE
- const isForeignTableSelected = entityType?.type === ENTITY_TYPE.FOREIGN_TABLE
const isLocked = EXCLUDED_SCHEMAS.includes(entityType?.schema ?? '')
const canEditViaTableEditor = isTableSelected && !isLocked
- // [Joshen] We can tweak below to eventually support composite keys as the data
- // returned from foreignKeyMeta should be easy to deal with, rather than pg-meta
- const formattedRelationships = (
- ('relationships' in selectedTable && selectedTable.relationships) ||
- []
- ).map((relationship: PostgresRelationship) => {
- const relationshipMeta = foreignKeyMeta.find(
- (fk: ForeignKeyConstraint) => fk.id === relationship.id
- )
- return {
- ...relationship,
- deletion_action: relationshipMeta?.deletion_action ?? FOREIGN_KEY_CASCADE_ACTION.NO_ACTION,
- }
+ const gridTable = getSupaTable({
+ encryptedColumns,
+ foreignKeyMeta,
+ selectedTable,
+ entityType: entityType?.type,
})
- const gridTable =
- !isViewSelected && !isForeignTableSelected
- ? parseSupaTable(
- {
- table: selectedTable as PostgresTable,
- columns: (selectedTable as PostgresTable).columns ?? [],
- primaryKeys: (selectedTable as PostgresTable).primary_keys ?? [],
- relationships: formattedRelationships,
- },
- encryptedColumns
- )
- : parseSupaTable({
- table: selectedTable as SchemaView,
- columns: (selectedTable as SchemaView).columns ?? [],
- primaryKeys: [],
- relationships: [],
- })
-
const gridKey = `${selectedTable.schema}_${selectedTable.name}`
const onTableCreated = (table: PostgresTable) => {
diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx
index 159f5bb7dae..8b807064bc6 100644
--- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx
+++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx
@@ -1,6 +1,8 @@
+import { Blocks, FileText, Lightbulb, List, Settings } from 'lucide-react'
+
import type { Route } from 'components/ui/ui.types'
+import { EditorIndexPageLink } from 'data/prefetchers/project.$ref.editor'
import type { Project } from 'data/projects/project-detail-query'
-import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import {
Auth,
Database,
@@ -11,8 +13,8 @@ import {
Storage,
TableEditor,
} from 'icons'
+import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import { ICON_SIZE, ICON_STROKE_WIDTH } from './NavigationBar'
-import { Settings, FileText, List, Lightbulb, Blocks } from 'lucide-react'
export const generateToolRoutes = (ref?: string, project?: Project): Route[] => {
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
@@ -24,6 +26,7 @@ export const generateToolRoutes = (ref?: string, project?: Project): Route[] =>
label: 'Table Editor',
icon:
,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/editor`),
+ linkComponent:
,
},
{
key: 'sql',
diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx
index ca8b6143169..758deedc315 100644
--- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx
+++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx
@@ -1,13 +1,18 @@
import { noop } from 'lodash'
import Link from 'next/link'
-import { AnchorHTMLAttributes, forwardRef } from 'react'
-import { cn } from 'ui'
-import { Tooltip_Shadcn_, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_ } from 'ui'
+import {
+ AnchorHTMLAttributes,
+ cloneElement,
+ ComponentPropsWithoutRef,
+ forwardRef,
+ isValidElement,
+} from 'react'
+import { cn, Tooltip_Shadcn_, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_ } from 'ui'
import type { Route } from 'components/ui/ui.types'
-import { useAppStateSnapshot } from 'state/app-state'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { LOCAL_STORAGE_KEYS } from 'lib/constants'
+import { useAppStateSnapshot } from 'state/app-state'
interface NavigationIconButtonProps extends AnchorHTMLAttributes
{
route: Route
@@ -41,8 +46,18 @@ const NavigationIconLink = forwardRef>(
+ function LinkComponent(props, ref) {
+ if (route.linkComponent && isValidElement(route.linkComponent)) {
+ return cloneElement(route.linkComponent, { ...props, ref })
+ }
+
+ return
+ }
+ )
+
const linkContent = (
-
{route.label}
-
+
)
if (!allowNavPanelToExpand) {
diff --git a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx
index 2a2f463ea13..98cd65ec2b4 100644
--- a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx
+++ b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx
@@ -46,6 +46,7 @@ import {
} from 'ui'
import { useProjectContext } from '../ProjectLayout/ProjectContext'
import { Markdown } from 'components/interfaces/Markdown'
+import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id'
export interface EntityListItemProps {
id: number
@@ -277,8 +278,9 @@ const EntityListItem: ItemRenderer = ({
}
return (
- = ({
)}
-
+
)
}
diff --git a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
index c859e571e41..82ff2ae0376 100644
--- a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
+++ b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
@@ -12,10 +12,10 @@ import SchemaSelector from 'components/ui/SchemaSelector'
import { useSchemasQuery } from 'data/database/schemas-query'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
-import { useTableQuery } from 'data/tables/table-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
+import useTable from 'hooks/misc/useTable'
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import {
@@ -96,15 +96,7 @@ const TableEditorMenu = () => {
)
const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
- const { data: selectedTable } = useTableQuery(
- {
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- id: Number(id),
- },
- // only run if we have a selected table
- { enabled: Boolean(id) }
- )
+ const { data: selectedTable } = useTable(id !== undefined ? Number(id) : undefined)
useEffect(() => {
if (selectedTable) {
diff --git a/apps/studio/components/ui/ui.types.ts b/apps/studio/components/ui/ui.types.ts
index 8ede34b7f18..2aee180817c 100644
--- a/apps/studio/components/ui/ui.types.ts
+++ b/apps/studio/components/ui/ui.types.ts
@@ -6,4 +6,5 @@ export interface Route {
icon: ReactNode
link?: string
disabled?: boolean
+ linkComponent?: ReactNode
}
diff --git a/apps/studio/data/database/entity-definitions-query.ts b/apps/studio/data/database/entity-definitions-query.ts
index e288f81fa0e..4506ea58ec9 100644
--- a/apps/studio/data/database/entity-definitions-query.ts
+++ b/apps/studio/data/database/entity-definitions-query.ts
@@ -32,13 +32,7 @@ with records as (
'create materialized view ', concat(nc.nspname, '.', c.relname), ' as',
pg_get_viewdef(concat(nc.nspname, '.', c.relname), true)
)
- when 'f' then pg_temp.pg_get_tabledef(
- concat(nc.nspname),
- concat(c.relname),
- false,
- 'FKEYS_INTERNAL',
- 'NO_TRIGGERS'
- )
+ when 'f' then concat('create foreign table ', nc.nspname, '.', c.relname, ' ( ... )')
when 'p' then pg_temp.pg_get_tabledef(
concat(nc.nspname),
concat(c.relname),
diff --git a/apps/studio/data/database/foreign-key-constraints-query.ts b/apps/studio/data/database/foreign-key-constraints-query.ts
index 3c348b11ec2..7c34c7894fd 100644
--- a/apps/studio/data/database/foreign-key-constraints-query.ts
+++ b/apps/studio/data/database/foreign-key-constraints-query.ts
@@ -1,5 +1,12 @@
-import { UseQueryOptions } from '@tanstack/react-query'
-import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
+import { QueryClient, UseQueryOptions } from '@tanstack/react-query'
+
+import { sqlKeys } from 'data/sql/keys'
+import {
+ executeSql,
+ ExecuteSqlData,
+ ExecuteSqlError,
+ useExecuteSqlQuery,
+} from '../sql/execute-sql-query'
type GetForeignKeyConstraintsVariables = {
schema?: string
@@ -96,6 +103,22 @@ export type ForeignKeyConstraintsVariables = GetForeignKeyConstraintsVariables &
export type ForeignKeyConstraintsData = ForeignKeyConstraint[]
export type ForeignKeyConstraintsError = ExecuteSqlError
+function select(
+ data:
+ | {
+ result: any
+ }
+ | undefined
+) {
+ return (data?.result ?? []).map((foreignKey: ForeignKeyConstraintRaw) => {
+ return {
+ ...foreignKey,
+ source_columns: foreignKey.source_columns.replace('{', '').replace('}', '').split(','),
+ target_columns: foreignKey.target_columns.replace('{', '').replace('}', '').split(','),
+ }
+ })
+}
+
export const useForeignKeyConstraintsQuery = <
TData extends ForeignKeyConstraintsData = ForeignKeyConstraintsData,
>(
@@ -107,19 +130,30 @@ export const useForeignKeyConstraintsQuery = <
projectRef,
connectionString,
sql: getForeignKeyConstraintsQuery({ schema }),
- queryKey: ['foreign-key-constraints'],
+ queryKey: ['foreign-key-constraints', schema],
},
{
- select(data) {
- return ((data as any)?.result ?? []).map((foreignKey: ForeignKeyConstraintRaw) => {
- return {
- ...foreignKey,
- source_columns: foreignKey.source_columns.replace('{', '').replace('}', '').split(','),
- target_columns: foreignKey.target_columns.replace('{', '').replace('}', '').split(','),
- }
- })
- },
+ select,
...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)
+}
diff --git a/apps/studio/data/database/schemas-query.ts b/apps/studio/data/database/schemas-query.ts
index 911f8fc88fa..22e27c76092 100644
--- a/apps/studio/data/database/schemas-query.ts
+++ b/apps/studio/data/database/schemas-query.ts
@@ -2,7 +2,12 @@ import pgMeta from '@supabase/pg-meta'
import { QueryClient, UseQueryOptions } from '@tanstack/react-query'
import { z } from 'zod'
-import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from 'data/sql/execute-sql-query'
+import {
+ executeSql,
+ ExecuteSqlData,
+ ExecuteSqlError,
+ useExecuteSqlQuery,
+} from 'data/sql/execute-sql-query'
import { sqlKeys } from 'data/sql/keys'
export type SchemasVariables = {
@@ -39,3 +44,20 @@ export const useSchemasQuery = (
export function invalidateSchemasQuery(client: QueryClient, projectRef: string | undefined) {
return client.invalidateQueries(sqlKeys.query(projectRef, ['schemas', 'list']))
}
+
+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
+ )
+ )
+}
diff --git a/apps/studio/data/encrypted-columns/encrypted-columns-query.ts b/apps/studio/data/encrypted-columns/encrypted-columns-query.ts
new file mode 100644
index 00000000000..e21e2685631
--- /dev/null
+++ b/apps/studio/data/encrypted-columns/encrypted-columns-query.ts
@@ -0,0 +1,69 @@
+import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
+
+import { executeSql } from 'data/sql/execute-sql-query'
+import type { ResponseError } from 'types'
+import { encryptedColumnKeys } from './keys'
+
+export type EncryptedColumnsVariables = {
+ projectRef?: string
+ connectionString?: string
+ schema?: string
+ tableName?: string
+}
+
+export async function getEncryptedColumns(
+ { projectRef, connectionString, schema, tableName }: EncryptedColumnsVariables,
+ signal?: AbortSignal
+) {
+ if (!tableName) return []
+
+ try {
+ const encryptedColumns = await executeSql(
+ {
+ projectRef,
+ connectionString,
+ sql: `select column_name as name from information_schema.columns where table_schema = '${schema}' and table_name = 'decrypted_${tableName}' and column_name like 'decrypted_%'`,
+ queryKey: ['encrypted-columns', tableName],
+ },
+ signal
+ )
+ return encryptedColumns.result.map((column: any) => column.name.split('decrypted_')[1])
+ } catch (error) {
+ console.error('Error fetching encrypted columns', error)
+ return []
+ }
+}
+
+export type EncryptedColumnsData = Awaited>
+export type EncryptedColumnsError = ResponseError
+
+export const useEncryptedColumnsQuery = (
+ { projectRef, connectionString, schema, tableName }: EncryptedColumnsVariables,
+ {
+ enabled = true,
+ ...options
+ }: UseQueryOptions = {}
+) =>
+ useQuery(
+ encryptedColumnKeys.list(projectRef, schema, tableName),
+ ({ signal }) =>
+ getEncryptedColumns({ projectRef, connectionString, schema, tableName }, signal),
+ { enabled: enabled && typeof projectRef !== 'undefined', ...options }
+ )
+
+export function prefetchEncryptedColumns(
+ client: QueryClient,
+ { projectRef, connectionString, schema, tableName }: EncryptedColumnsVariables
+) {
+ return client.fetchQuery(encryptedColumnKeys.list(projectRef, schema, tableName), ({ signal }) =>
+ getEncryptedColumns(
+ {
+ projectRef,
+ connectionString,
+ schema,
+ tableName,
+ },
+ signal
+ )
+ )
+}
diff --git a/apps/studio/data/encrypted-columns/keys.ts b/apps/studio/data/encrypted-columns/keys.ts
new file mode 100644
index 00000000000..e73d19d7a53
--- /dev/null
+++ b/apps/studio/data/encrypted-columns/keys.ts
@@ -0,0 +1,7 @@
+export const encryptedColumnKeys = {
+ list: (
+ projectRef: string | undefined,
+ schema: string | undefined,
+ tableName: string | undefined
+ ) => ['projects', projectRef, 'encrypted-columns', schema, tableName] as const,
+}
diff --git a/apps/studio/data/entity-types/entity-type-query.ts b/apps/studio/data/entity-types/entity-type-query.ts
index c650159ce52..b0e26577073 100644
--- a/apps/studio/data/entity-types/entity-type-query.ts
+++ b/apps/studio/data/entity-types/entity-type-query.ts
@@ -1,5 +1,12 @@
-import { UseQueryOptions } from '@tanstack/react-query'
-import { ExecuteSqlData, ExecuteSqlError, useExecuteSqlQuery } from '../sql/execute-sql-query'
+import { QueryClient, UseQueryOptions } from '@tanstack/react-query'
+
+import { sqlKeys } from 'data/sql/keys'
+import {
+ executeSql,
+ ExecuteSqlData,
+ ExecuteSqlError,
+ useExecuteSqlQuery,
+} from '../sql/execute-sql-query'
import { ENTITY_TYPE } from './entity-type-constants'
type EntityTypeArgs = {
@@ -75,3 +82,22 @@ export const useEntityTypeQuery =
+ executeSql(
+ {
+ projectRef,
+ connectionString,
+ sql: entityTypeSqlQuery({ id }),
+ queryKey: ['entity-type', id],
+ },
+ signal
+ )
+ )
+ .then((data: { result: EntityTypeData[] }) => data?.result?.[0] ?? null)
+}
diff --git a/apps/studio/data/entity-types/entity-types-infinite-query.ts b/apps/studio/data/entity-types/entity-types-infinite-query.ts
index 3931fb892e9..91d1b549bd9 100644
--- a/apps/studio/data/entity-types/entity-types-infinite-query.ts
+++ b/apps/studio/data/entity-types/entity-types-infinite-query.ts
@@ -1,7 +1,8 @@
-import { useInfiniteQuery, UseInfiniteQueryOptions } from '@tanstack/react-query'
+import { QueryClient, useInfiniteQuery, UseInfiniteQueryOptions } from '@tanstack/react-query'
import { executeSql, ExecuteSqlVariables } from 'data/sql/execute-sql-query'
import type { Entity } from './entity-type-query'
import { entityTypeKeys } from './keys'
+import { ENTITY_TYPE } from './entity-type-constants'
export type EntityTypesVariables = {
projectRef?: string
@@ -29,7 +30,7 @@ export async function getEntityTypes(
limit = 100,
page = 0,
sort = 'alphabetical',
- filterTypes,
+ filterTypes = Object.values(ENTITY_TYPE),
}: EntityTypesVariables,
signal?: AbortSignal
) {
@@ -57,7 +58,7 @@ export async function getEntityTypes(
pg_namespace nc
join pg_class c on nc.oid = c.relnamespace
where
- c.relkind in (${filterTypes === undefined ? `'r', 'v', 'm', 'f', 'p'` : filterTypes.map((x) => `'${x}'`).join(', ')})
+ c.relkind in (${filterTypes.map((x) => `'${x}'`).join(', ')})
and not pg_is_other_temp_schema(nc.oid)
and (
pg_has_role(c.relowner, 'USAGE')
@@ -155,3 +156,34 @@ export const useEntityTypesQuery = (
...options,
}
)
+
+export function prefetchEntityTypes(
+ client: QueryClient,
+ {
+ projectRef,
+ connectionString,
+ schemas = ['public'],
+ search,
+ limit = 100,
+ sort,
+ filterTypes,
+ }: Omit
+) {
+ return client.prefetchInfiniteQuery(
+ entityTypeKeys.list(projectRef, { schemas, search, sort, limit, filterTypes }),
+ ({ signal, pageParam }) =>
+ getEntityTypes(
+ {
+ projectRef,
+ connectionString,
+ schemas,
+ search,
+ limit,
+ page: pageParam,
+ sort,
+ filterTypes,
+ },
+ signal
+ )
+ )
+}
diff --git a/apps/studio/data/foreign-tables/foreign-table-query.ts b/apps/studio/data/foreign-tables/foreign-table-query.ts
index d6e3af3748f..cc7370d4ff8 100644
--- a/apps/studio/data/foreign-tables/foreign-table-query.ts
+++ b/apps/studio/data/foreign-tables/foreign-table-query.ts
@@ -1,5 +1,5 @@
import type { PostgresTable } from '@supabase/postgres-meta'
-import { useQuery, UseQueryOptions } from '@tanstack/react-query'
+import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get } from 'lib/common/fetch'
import { API_URL } from 'lib/constants'
import { foreignTableKeys } from './keys'
@@ -56,3 +56,12 @@ export const useForeignTableQuery = (
...options,
}
)
+
+export function prefetchForeignTable(
+ client: QueryClient,
+ { projectRef, connectionString, id }: ForeignTableVariables
+) {
+ return client.fetchQuery(foreignTableKeys.foreignTable(projectRef, id), ({ signal }) =>
+ getForeignTable({ id, projectRef, connectionString }, signal)
+ )
+}
diff --git a/apps/studio/data/materialized-views/materialized-view-query.ts b/apps/studio/data/materialized-views/materialized-view-query.ts
index 0b714667b9b..1d6e2411e24 100644
--- a/apps/studio/data/materialized-views/materialized-view-query.ts
+++ b/apps/studio/data/materialized-views/materialized-view-query.ts
@@ -1,5 +1,6 @@
import type { PostgresMaterializedView } from '@supabase/postgres-meta'
-import { useQuery, UseQueryOptions } from '@tanstack/react-query'
+import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
+
import { get } from 'lib/common/fetch'
import { API_URL } from 'lib/constants'
import { materializedViewKeys } from './keys'
@@ -59,3 +60,12 @@ export const useMaterializedViewQuery = (
...options,
}
)
+
+export function prefetchMaterializedView(
+ client: QueryClient,
+ { projectRef, connectionString, id }: MaterializedViewVariables
+) {
+ return client.fetchQuery(materializedViewKeys.materializedView(projectRef, id), ({ signal }) =>
+ getMaterializedView({ id, projectRef, connectionString }, signal)
+ )
+}
diff --git a/apps/studio/data/prefetchers/project.$ref.editor.$id.tsx b/apps/studio/data/prefetchers/project.$ref.editor.$id.tsx
new file mode 100644
index 00000000000..ad4ed80b1fe
--- /dev/null
+++ b/apps/studio/data/prefetchers/project.$ref.editor.$id.tsx
@@ -0,0 +1,182 @@
+import { useQueryClient } from '@tanstack/react-query'
+import Link from 'next/link'
+import { useRouter } from 'next/router'
+import { ComponentProps, PropsWithChildren, useCallback } from 'react'
+
+import { loadTableEditorSortsAndFiltersFromLocalStorage } from 'components/grid/SupabaseGrid'
+import {
+ formatFilterURLParams,
+ formatSortURLParams,
+ getSupaTable,
+} from 'components/grid/SupabaseGrid.utils'
+import { Filter, Sort } from 'components/grid/types'
+import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import {
+ ForeignKeyConstraintsData,
+ prefetchForeignKeyConstraints,
+} from 'data/database/foreign-key-constraints-query'
+import { prefetchEncryptedColumns } from 'data/encrypted-columns/encrypted-columns-query'
+import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
+import { Entity, prefetchEntityType } from 'data/entity-types/entity-type-query'
+import { ForeignTableData, prefetchForeignTable } from 'data/foreign-tables/foreign-table-query'
+import {
+ MaterializedViewData,
+ prefetchMaterializedView,
+} from 'data/materialized-views/materialized-view-query'
+import { prefetchTableRows } from 'data/table-rows/table-rows-query'
+import { prefetchTable, TableData } from 'data/tables/table-query'
+import { prefetchView, ViewData } from 'data/views/view-query'
+import { useFlag } from 'hooks/ui/useFlag'
+import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state'
+import { TABLE_EDITOR_DEFAULT_ROWS_PER_PAGE } from 'state/table-editor'
+
+export function usePrefetchEditorTablePage() {
+ const router = useRouter()
+ const queryClient = useQueryClient()
+ const { project } = useProjectContext()
+ const roleImpersonationState = useRoleImpersonationStateSnapshot()
+ const tableEditorPrefetchingEnabled = useFlag('tableEditorPrefetching')
+
+ return useCallback(
+ ({ id: _id, filters, sorts }: { id?: string; filters?: Filter[]; sorts?: Sort[] }) => {
+ if (!tableEditorPrefetchingEnabled) return
+
+ const id = _id ? Number(_id) : undefined
+ if (!project || !id || isNaN(id)) return
+
+ // Prefetch the code
+ router.prefetch(`/project/${project.ref}/editor/${id}`)
+
+ // Prefetch the data
+ prefetchEntityType(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ id,
+ })
+ .then((entity) => {
+ let promises: [
+ Promise,
+ Promise,
+ Promise,
+ (
+ | Promise
+ | undefined
+ ),
+ ] = [
+ Promise.resolve(entity),
+ prefetchForeignKeyConstraints(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ schema: entity?.schema,
+ }),
+ prefetchEncryptedColumns(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ schema: entity?.schema,
+ tableName: entity?.name,
+ }),
+ undefined,
+ ]
+
+ switch (entity?.type) {
+ case ENTITY_TYPE.TABLE:
+ case ENTITY_TYPE.PARTITIONED_TABLE:
+ promises[3] = prefetchTable(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ id,
+ })
+ break
+
+ case ENTITY_TYPE.VIEW:
+ promises[3] = prefetchView(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ id,
+ })
+ break
+
+ case ENTITY_TYPE.MATERIALIZED_VIEW:
+ promises[3] = prefetchMaterializedView(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ id,
+ })
+ break
+
+ case ENTITY_TYPE.FOREIGN_TABLE:
+ promises[3] = prefetchForeignTable(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ id,
+ })
+ break
+ }
+
+ return Promise.all(promises)
+ })
+ .then(([entity, foreignKeyConstraints, encryptedColumns, table]) => {
+ if (entity && table) {
+ const supaTable = getSupaTable({
+ selectedTable: table,
+ encryptedColumns,
+ foreignKeyMeta: foreignKeyConstraints,
+ entityType: entity.type,
+ })
+
+ const { sorts: localSorts = [], filters: localFilters = [] } =
+ loadTableEditorSortsAndFiltersFromLocalStorage(
+ project.ref,
+ entity.name,
+ entity.schema
+ ) ?? {}
+
+ prefetchTableRows(queryClient, {
+ queryKey: [supaTable.schema, supaTable.name],
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ table: supaTable,
+ sorts: sorts ?? formatSortURLParams(supaTable.name, localSorts),
+ filters: filters ?? formatFilterURLParams(localFilters),
+ page: 1,
+ limit: TABLE_EDITOR_DEFAULT_ROWS_PER_PAGE,
+ impersonatedRole: roleImpersonationState.role,
+ })
+ }
+ })
+ },
+ [project, queryClient, router]
+ )
+}
+
+type LinkProps = ComponentProps
+
+interface EditorTablePageLinkProps extends Omit {
+ projectRef?: string
+ id?: string
+ sorts?: Sort[]
+ filters?: Filter[]
+ href?: LinkProps['href']
+}
+
+export function EditorTablePageLink({
+ projectRef,
+ id,
+ sorts,
+ filters,
+ href,
+ children,
+ ...props
+}: PropsWithChildren) {
+ const prefetch = usePrefetchEditorTablePage()
+
+ return (
+ prefetch({ id, sorts, filters })}
+ {...props}
+ >
+ {children}
+
+ )
+}
diff --git a/apps/studio/data/prefetchers/project.$ref.editor.tsx b/apps/studio/data/prefetchers/project.$ref.editor.tsx
new file mode 100644
index 00000000000..e21d6fed797
--- /dev/null
+++ b/apps/studio/data/prefetchers/project.$ref.editor.tsx
@@ -0,0 +1,64 @@
+import { useQueryClient } from '@tanstack/react-query'
+import Link from 'next/link'
+import { ComponentProps, PropsWithChildren, useCallback } from 'react'
+
+import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import { prefetchSchemas } from 'data/database/schemas-query'
+import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
+import { prefetchEntityTypes } from 'data/entity-types/entity-types-infinite-query'
+import { useLocalStorage } from 'hooks/misc/useLocalStorage'
+import { useFlag } from 'hooks/ui/useFlag'
+import { useRouter } from 'next/router'
+
+export function usePrefetchEditorIndexPage() {
+ const router = useRouter()
+ const queryClient = useQueryClient()
+ const { project } = useProjectContext()
+ const tableEditorPrefetchingEnabled = useFlag('tableEditorPrefetching')
+
+ const [entityTypesSort] = useLocalStorage<'alphabetical' | 'grouped-alphabetical'>(
+ 'table-editor-sort',
+ 'alphabetical'
+ )
+
+ return useCallback(() => {
+ if (!tableEditorPrefetchingEnabled || !project) return
+
+ // Prefetch code
+ router.prefetch(`/project/${project.ref}/editor`)
+
+ // Prefetch data
+ prefetchSchemas(queryClient, {
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ })
+ prefetchEntityTypes(queryClient, {
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ sort: entityTypesSort,
+ filterTypes: Object.values(ENTITY_TYPE),
+ })
+ }, [entityTypesSort, project, queryClient, router])
+}
+
+type LinkProps = ComponentProps
+
+interface EditorIndexPageLinkProps extends Omit {
+ projectRef?: string
+ href?: LinkProps['href']
+}
+
+export function EditorIndexPageLink({
+ href,
+ projectRef,
+ children,
+ ...props
+}: PropsWithChildren) {
+ const prefetch = usePrefetchEditorIndexPage()
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/apps/studio/data/table-rows/table-rows-query.ts b/apps/studio/data/table-rows/table-rows-query.ts
index dfa8e2b3dc4..39ecb7dce9a 100644
--- a/apps/studio/data/table-rows/table-rows-query.ts
+++ b/apps/studio/data/table-rows/table-rows-query.ts
@@ -1,4 +1,4 @@
-import type { QueryKey, UseQueryOptions } from '@tanstack/react-query'
+import type { QueryClient, QueryKey, UseQueryOptions } from '@tanstack/react-query'
import { IS_PLATFORM } from 'common'
import { Query } from 'components/grid/query/Query'
@@ -7,13 +7,17 @@ import {
JSON_TYPES,
TEXT_TYPES,
} from 'components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.constants'
+import { sqlKeys } from 'data/sql/keys'
import { KB } from 'lib/constants'
import {
ImpersonationRole,
ROLE_IMPERSONATION_NO_RESULTS,
wrapWithRoleImpersonation,
} from 'lib/role-impersonation'
-import { useIsRoleImpersonationEnabled } from 'state/role-impersonation-state'
+import {
+ isRoleImpersonationEnabled,
+ useIsRoleImpersonationEnabled,
+} from 'state/role-impersonation-state'
import {
ExecuteSqlData,
ExecuteSqlError,
@@ -204,6 +208,21 @@ export type TableRowsVariables = GetTableRowsArgs & {
export type TableRowsData = TableRows
export type TableRowsError = ExecuteSqlError
+function getTableRowsQueryKey({ queryKey, table, impersonatedRole, ...args }: TableRowsVariables) {
+ return [
+ ...(queryKey ?? []),
+ {
+ table: {
+ name: table?.name,
+ schema: table?.schema,
+ columns: table?.columns.map((c) => c.name),
+ },
+ impersonatedRole,
+ ...args,
+ },
+ ]
+}
+
export const useTableRowsQuery = (
{ projectRef, connectionString, queryKey, table, impersonatedRole, ...args }: TableRowsVariables,
options: UseQueryOptions = {}
@@ -218,18 +237,7 @@ export const useTableRowsQuery = (
projectRef: projectRef ?? 'ref',
role: impersonatedRole,
}),
- queryKey: [
- ...(queryKey ?? []),
- {
- table: {
- name: table?.name,
- schema: table?.schema,
- columns: table?.columns.map((c) => c.name),
- },
- impersonatedRole,
- ...args,
- },
- ],
+ queryKey: getTableRowsQueryKey({ queryKey, table, impersonatedRole, ...args }),
isRoleImpersonationEnabled,
},
{
@@ -247,3 +255,26 @@ export const useTableRowsQuery = (
}
)
}
+
+export function prefetchTableRows(
+ client: QueryClient,
+ { projectRef, connectionString, queryKey, table, impersonatedRole, ...args }: TableRowsVariables
+) {
+ return client.fetchQuery(
+ sqlKeys.query(projectRef, getTableRowsQueryKey({ queryKey, table, impersonatedRole, ...args })),
+ ({ signal }) =>
+ executeSql(
+ {
+ projectRef,
+ connectionString,
+ sql: wrapWithRoleImpersonation(getTableRowsSqlQuery({ table, ...args }), {
+ projectRef: projectRef ?? 'ref',
+ role: impersonatedRole,
+ }),
+ queryKey: getTableRowsQueryKey({ queryKey, table, impersonatedRole, ...args }),
+ isRoleImpersonationEnabled: isRoleImpersonationEnabled(impersonatedRole),
+ },
+ signal
+ )
+ )
+}
diff --git a/apps/studio/data/tables/table-query.ts b/apps/studio/data/tables/table-query.ts
index 2e8bea2b5dd..36b5d4195a3 100644
--- a/apps/studio/data/tables/table-query.ts
+++ b/apps/studio/data/tables/table-query.ts
@@ -1,6 +1,5 @@
import type { PostgresTable } from '@supabase/postgres-meta'
-import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query'
-import { useCallback } from 'react'
+import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import { tableKeys } from './keys'
@@ -57,23 +56,11 @@ export const useTableQuery = (
}
)
-/**
- * useGetTable
- * Tries to get a table from the react-query cache, or loads it from the server if it's not cached.
- */
-export function useGetTable({
- projectRef,
- connectionString,
-}: Pick) {
- const queryClient = useQueryClient()
-
- return useCallback(
- (id: NonNullable) => {
- return queryClient.fetchQuery({
- queryKey: tableKeys.table(projectRef, id),
- queryFn: ({ signal }) => getTable({ id, projectRef, connectionString }, signal),
- })
- },
- [connectionString, projectRef, queryClient]
+export function prefetchTable(
+ client: QueryClient,
+ { projectRef, connectionString, id }: TableVariables
+) {
+ return client.fetchQuery(tableKeys.table(projectRef, id), ({ signal }) =>
+ getTable({ id, projectRef, connectionString }, signal)
)
}
diff --git a/apps/studio/data/views/view-query.ts b/apps/studio/data/views/view-query.ts
index ae6d11b6c2d..9fc16cf2299 100644
--- a/apps/studio/data/views/view-query.ts
+++ b/apps/studio/data/views/view-query.ts
@@ -1,5 +1,5 @@
import type { PostgresView } from '@supabase/postgres-meta'
-import { useQuery, UseQueryOptions } from '@tanstack/react-query'
+import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import type { ResponseError } from 'types'
@@ -55,3 +55,12 @@ export const useViewQuery = (
...options,
}
)
+
+export function prefetchView(
+ client: QueryClient,
+ { projectRef, connectionString, id }: ViewVariables
+) {
+ return client.fetchQuery(viewKeys.view(projectRef, id), ({ signal }) =>
+ getView({ id, projectRef, connectionString }, signal)
+ )
+}
diff --git a/apps/studio/hooks/misc/useTable.ts b/apps/studio/hooks/misc/useTable.ts
index 17bf4042be1..891d25fe9df 100644
--- a/apps/studio/hooks/misc/useTable.ts
+++ b/apps/studio/hooks/misc/useTable.ts
@@ -25,18 +25,7 @@ function useTable(id?: number) {
id: entity?.id,
},
{
- enabled: entity?.type === ENTITY_TYPE.TABLE,
- }
- )
-
- const partitionTableResult = useTableQuery(
- {
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- id: entity?.id,
- },
- {
- enabled: entity?.type === ENTITY_TYPE.PARTITIONED_TABLE,
+ enabled: entity?.type === ENTITY_TYPE.TABLE || entity?.type === ENTITY_TYPE.PARTITIONED_TABLE,
}
)
@@ -75,9 +64,8 @@ function useTable(id?: number) {
switch (entity?.type) {
case ENTITY_TYPE.TABLE:
- return tableResult
case ENTITY_TYPE.PARTITIONED_TABLE:
- return partitionTableResult
+ return tableResult
case ENTITY_TYPE.VIEW:
return viewResult
case ENTITY_TYPE.MATERIALIZED_VIEW:
diff --git a/apps/studio/pages/project/[ref]/editor/[id].tsx b/apps/studio/pages/project/[ref]/editor/[id].tsx
index 69240d08908..9c232bb3883 100644
--- a/apps/studio/pages/project/[ref]/editor/[id].tsx
+++ b/apps/studio/pages/project/[ref]/editor/[id].tsx
@@ -31,7 +31,7 @@ const TableEditorPage: NextPageWithLayout = () => {
if (tables.length > 0) {
router.push(`/project/${projectRef}/editor/${tables[0].id}`)
} else {
- router.push(`/project/${projectRef}/editor/`)
+ router.push(`/project/${projectRef}/editor`)
}
}}
/>
diff --git a/apps/studio/state/table-editor.tsx b/apps/studio/state/table-editor.tsx
index a55245d9ba3..05367f0153f 100644
--- a/apps/studio/state/table-editor.tsx
+++ b/apps/studio/state/table-editor.tsx
@@ -6,6 +6,8 @@ import { PropsWithChildren, createContext, useContext, useRef } from 'react'
import type { Dictionary } from 'types'
import { proxy, useSnapshot } from 'valtio'
+export const TABLE_EDITOR_DEFAULT_ROWS_PER_PAGE = 100
+
type ForeignKeyState = {
foreignKey: ForeignKey
row: Dictionary
@@ -63,7 +65,7 @@ export const createTableEditorState = () => {
setPage: (page: number) => {
state.page = page
},
- rowsPerPage: 100,
+ rowsPerPage: TABLE_EDITOR_DEFAULT_ROWS_PER_PAGE,
setRowsPerPage: (rowsPerPage: number) => {
state.rowsPerPage = rowsPerPage
},