From df63ce36586e8a19db4346e7c21893b68f044386 Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:14:30 -0500 Subject: [PATCH] fix(mssql foreign tables): disallow sort on columns filtered for equality (#40137) There is an edge case interaction between the Postgres query parser and MSSQL foreign tables, where the query parser may drop sort clauses that are redundant with applied filters. This leads to invalid MSSQL syntax, because the resulting query has a `limit` but no `sort`, and the user sees a confusing error message. This PR detects this edge case on MSSQL foreign tables. There are three cases: 1. The user filters by a column, but there are still other columns available for sorting. The default search for a sorting column will leave out the filtered column. 2. The user filters by a column/several columns, and there are no more columns that can be used for sorting. We stop the query and show an admonition. 3. The user filters by a column, then tries to sort by the same column. We stop the query and show an admonition. --- .../components/grid/MsSqlValidation.tsx | 77 +++++++++++++++++++ apps/studio/components/grid/SupabaseGrid.tsx | 19 ++++- .../grid/components/footer/Footer.tsx | 8 +- .../footer/pagination/Pagination.tsx | 12 ++- .../components/grid/components/grid/Grid.tsx | 4 +- .../grid/components/header/Header.tsx | 37 +++++---- .../SidePanelEditor/SidePanelEditor.tsx | 2 +- .../SidePanelEditor/SidePanelEditor.utils.tsx | 4 +- .../TableEditor/TableEditor.tsx | 2 +- .../table-editor/table-editor-query-sql.ts | 12 ++- .../data/table-editor/table-editor-types.ts | 8 ++ .../data/table-rows/table-rows-query.ts | 22 +++++- .../data/tables/table-create-mutation.ts | 2 +- .../pg-meta/src/pg-meta-foreign-tables.ts | 11 ++- packages/pg-meta/src/pg-meta-tables.ts | 26 +++---- packages/pg-meta/src/query/table-row-query.ts | 26 +++++-- packages/pg-meta/src/sql/foreign-tables.ts | 9 ++- packages/pg-meta/src/sql/tables.ts | 2 +- packages/pg-meta/test/foreign-tables.test.ts | 16 +++- .../test/query/table-row-query.test.ts | 23 ++++++ packages/pg-meta/test/tables.test.ts | 5 +- 21 files changed, 267 insertions(+), 60 deletions(-) create mode 100644 apps/studio/components/grid/MsSqlValidation.tsx 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 => ( +