diff --git a/apps/studio/components/grid/MsSqlValidation.tsx b/apps/studio/components/grid/MsSqlValidation.tsx new file mode 100644 index 00000000000..b6951f4c514 --- /dev/null +++ b/apps/studio/components/grid/MsSqlValidation.tsx @@ -0,0 +1,77 @@ +import { isMsSqlForeignTable, type Entity } from 'data/table-editor/table-editor-types' +import type { ComponentType, ReactNode } from 'react' + +import { Admonition } from 'ui-patterns' +import type { Filter, Sort } from './types' + +type ValidateMsSqlSortingParams = { + filters: Filter[] + sorts: Sort[] + table: Entity +} + +type MsSqlWarning = 'ConflictingSort' | 'NoValidSortPossible' + +/** + * There is an edge case with the Postgres query planner and MS SQL foreign + * tables, where the Postgres query planner will drop sort clauses that are + * redundant with filters, resulting in invalid MS SQL syntax. We want to + * detect any conflicting or impossible sorts on filtered columns when the + * table is an MS SQL foreign table, and warn the user. + */ +export const validateMsSqlSorting = ({ + filters, + sorts, + table, +}: ValidateMsSqlSortingParams): + | { warning: MsSqlWarning; Component: ComponentType } + | { warning: null } => { + const isMsSql = isMsSqlForeignTable(table) + if (!isMsSql) return { warning: null } + + const equalityFilterColumns = new Set( + filters + .filter((filter) => filter.operator === '=' || filter.operator === 'is') + .map((filter) => filter.column) + ) + + const conflictingSort = + sorts.length > 0 && sorts.every((sort) => equalityFilterColumns.has(sort.column)) + const showMsSqlSortWarning = equalityFilterColumns.size > 0 && !!conflictingSort + if (showMsSqlSortWarning) + return { warning: 'ConflictingSort', Component: MsSqlSortWarningAdmonition } + + const noSortColumnsRemaining = + table.columns.length > 0 && + table.columns.every((col) => col.data_type === 'json' || equalityFilterColumns.has(col.name)) + const showMsSqlNoValidSortWarning = filters.length > 0 && noSortColumnsRemaining + if (showMsSqlNoValidSortWarning) + return { warning: 'NoValidSortPossible', Component: MsSqlNoValidSortAdmonition } + + return { warning: null } +} + +type MsSqlAdmonitionProps = { + title: string + children: string +} + +const MsSqlAdmonition = ({ title, children }: MsSqlAdmonitionProps): ReactNode => ( +
+ +
+) + +const MsSqlSortWarningAdmonition = (): ReactNode => ( + + Sorting only by columns filtered with "=" or "is" doesn't work on MSSQL tables. Pick a different + sorting column, or add a column not in your filter. + +) + +const MsSqlNoValidSortAdmonition = (): ReactNode => ( + + All columns that can be sorted have been filtered with "=" or "is", which doesn't work on MSSQL + tables. Remove a column from your filter to continue. + +) diff --git a/apps/studio/components/grid/SupabaseGrid.tsx b/apps/studio/components/grid/SupabaseGrid.tsx index 44eaa5de10f..6862463414f 100644 --- a/apps/studio/components/grid/SupabaseGrid.tsx +++ b/apps/studio/components/grid/SupabaseGrid.tsx @@ -5,6 +5,7 @@ import { HTML5Backend } from 'react-dnd-html5-backend' import { createPortal } from 'react-dom' import { useParams } from 'common' +import { isMsSqlForeignTable } from 'data/table-editor/table-editor-types' import { useTableRowsQuery } from 'data/table-rows/table-rows-query' import { RoleImpersonationState } from 'lib/role-impersonation' import { EMPTY_ARR } from 'lib/void' @@ -22,6 +23,7 @@ import { GridProps } from './types' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { useTableFilter } from './hooks/useTableFilter' import { useTableSort } from './hooks/useTableSort' +import { validateMsSqlSorting } from './MsSqlValidation' export const SupabaseGrid = ({ customHeader, @@ -47,6 +49,11 @@ export const SupabaseGrid = ({ const roleImpersonationState = useRoleImpersonationStateSnapshot() + const msSqlWarning = isMsSqlForeignTable(snap.originalTable) + ? validateMsSqlSorting({ filters, sorts, table: snap.originalTable }) + : { warning: null } + const tableQueriesEnabled = msSqlWarning.warning === null + const { data, error, isSuccess, isError, isLoading, isRefetching } = useTableRowsQuery( { projectRef: project?.ref, @@ -60,6 +67,7 @@ export const SupabaseGrid = ({ }, { keepPreviousData: true, + enabled: tableQueriesEnabled, retry: (_, error: any) => { const doesNotExistError = error && error.message?.includes('does not exist') if (doesNotExistError) onApplySorts([]) @@ -77,7 +85,13 @@ export const SupabaseGrid = ({ return (
-
+
+ + {msSqlWarning.warning !== null && } {children || ( <> @@ -86,13 +100,14 @@ export const SupabaseGrid = ({ {...gridProps} rows={rows} error={error} + isDisabled={!tableQueriesEnabled} isLoading={isLoading} isSuccess={isSuccess} isError={isError} filters={filters} onApplyFilters={onApplyFilters} /> -