diff --git a/apps/studio/components/grid/SupabaseGrid.tsx b/apps/studio/components/grid/SupabaseGrid.tsx index 0d15e15667b..7f2b1c374f8 100644 --- a/apps/studio/components/grid/SupabaseGrid.tsx +++ b/apps/studio/components/grid/SupabaseGrid.tsx @@ -133,7 +133,7 @@ const SupabaseGridLayout = (props: SupabaseGridProps) => { const [{ sort, filter }, setParams] = useUrlState({ arrayKeys: ['sort', 'filter'], }) - const sorts = formatSortURLParams(sort as string[]) + const sorts = formatSortURLParams(props.table.name, sort as string[] | undefined) const filters = formatFilterURLParams(filter as string[]) const roleImpersonationState = useRoleImpersonationStateSnapshot() diff --git a/apps/studio/components/grid/SupabaseGrid.utils.ts b/apps/studio/components/grid/SupabaseGrid.utils.ts index 164f03ae81a..a8dadb0c5df 100644 --- a/apps/studio/components/grid/SupabaseGrid.utils.ts +++ b/apps/studio/components/grid/SupabaseGrid.utils.ts @@ -1,10 +1,11 @@ import AwesomeDebouncePromise from 'awesome-debounce-promise' +import type { Filter } from 'components/grid/types' +import { compact } from 'lodash' +import type { Dictionary } from 'types' +import { FilterOperatorOptions } from './components/header/filter' import { STORAGE_KEY_PREFIX } from './constants' import { InitialStateType } from './store/reducers' import type { Sort, SupabaseGridProps, SupaColumn, SupaTable } from './types' -import type { Dictionary } from 'types' -import { FilterOperatorOptions } from './components/header/filter' -import type { Filter } from 'components/grid/types' /** * Ensure that if editable is false, we should remove all editing actions @@ -26,19 +27,18 @@ export function cleanupProps(props: SupabaseGridProps) { } } -export function formatSortURLParams(sort?: string[]) { - return ( - Array.isArray(sort) - ? sort - .map((s) => { - const [column, order] = s.split(':') - // Reject any possible malformed sort param - if (!column || !order) return undefined - else return { column, ascending: order === 'asc' } - }) - .filter((s) => s !== undefined) - : [] - ) as Sort[] +export function formatSortURLParams(tableName: string, sort?: string[]): Sort[] { + if (Array.isArray(sort)) { + return compact( + sort.map((s) => { + const [column, order] = s.split(':') + // Reject any possible malformed sort param + if (!column || !order) return undefined + else return { table: tableName, column, ascending: order === 'asc' } + }) + ) + } + return [] } export function formatFilterURLParams(filter?: string[]): Filter[] { diff --git a/apps/studio/components/grid/components/header/sort/SortPopover.tsx b/apps/studio/components/grid/components/header/sort/SortPopover.tsx index e8039c1041a..be08f894b07 100644 --- a/apps/studio/components/grid/components/header/sort/SortPopover.tsx +++ b/apps/studio/components/grid/components/header/sort/SortPopover.tsx @@ -66,8 +66,8 @@ export interface SortOverlayProps { const SortOverlay = ({ table, sorts: sortsFromUrl, onApplySorts }: SortOverlayProps) => { const initialSorts = useMemo( - () => formatSortURLParams((sortsFromUrl as string[]) ?? []), - [sortsFromUrl] + () => formatSortURLParams(table.name, sortsFromUrl ?? []), + [table.name, sortsFromUrl] ) const [sorts, setSorts] = useState(initialSorts) @@ -82,7 +82,7 @@ const SortOverlay = ({ table, sorts: sortsFromUrl, onApplySorts }: SortOverlayPr }) || [] function onAddSort(columnName: string | number) { - setSorts([...sorts, { column: columnName as string, ascending: true }]) + setSorts([...sorts, { table: table.name, column: columnName as string, ascending: true }]) } const onDeleteSort = useCallback((column: string) => { diff --git a/apps/studio/components/grid/query/Query.utils.ts b/apps/studio/components/grid/query/Query.utils.ts index fcfd7f05eca..9fc18e5f92f 100644 --- a/apps/studio/components/grid/query/Query.utils.ts +++ b/apps/studio/components/grid/query/Query.utils.ts @@ -1,6 +1,6 @@ -import { ident, literal, format } from '@scaleleap/pg-format' -import type { Filter, QueryPagination, QueryTable, Sort } from '../types' +import { format, ident, literal } from '@scaleleap/pg-format' import type { Dictionary } from 'types' +import type { Filter, QueryPagination, QueryTable, Sort } from '../types' export function countQuery( table: QueryTable, @@ -223,7 +223,7 @@ function applySorts(query: string, sorts: Sort[]) { .map((x) => { const order = x.ascending ? 'asc' : 'desc' const nullOrder = x.nullsFirst ? 'nulls first' : 'nulls last' - return `${ident(x.column)} ${order} ${nullOrder}` + return `${ident(x.table)}.${ident(x.column)} ${order} ${nullOrder}` }) .join(', ')}` return query diff --git a/apps/studio/components/grid/query/QueryFilter.ts b/apps/studio/components/grid/query/QueryFilter.ts index 03de13571ac..f3754b9f27c 100644 --- a/apps/studio/components/grid/query/QueryFilter.ts +++ b/apps/studio/components/grid/query/QueryFilter.ts @@ -1,11 +1,11 @@ -import type { Filter, FilterOperator, QueryTable, Sort } from '../types' import type { Dictionary } from 'types' +import type { Filter, FilterOperator, QueryTable, Sort } from '../types' import { IQueryModifier, QueryModifier } from './QueryModifier' export interface IQueryFilter { filter: (column: string, operator: FilterOperator, value: string) => IQueryFilter match: (criteria: Dictionary) => IQueryFilter - order: (column: string, ascending?: boolean, nullsFirst?: boolean) => IQueryFilter + order: (table: string, column: string, ascending?: boolean, nullsFirst?: boolean) => IQueryFilter } export class QueryFilter implements IQueryFilter, IQueryModifier { @@ -31,8 +31,9 @@ export class QueryFilter implements IQueryFilter, IQueryModifier { return this } - order(column: string, ascending = true, nullsFirst = false) { + order(table: string, column: string, ascending = true, nullsFirst = false) { this.sorts.push({ + table: table, column: column, ascending, nullsFirst, diff --git a/apps/studio/components/grid/types/base.ts b/apps/studio/components/grid/types/base.ts index 347bf3d6756..262d4c81e3c 100644 --- a/apps/studio/components/grid/types/base.ts +++ b/apps/studio/components/grid/types/base.ts @@ -1,6 +1,7 @@ import { CalculatedColumn, RenderHeaderCellProps } from 'react-data-grid' export interface Sort { + table: string column: string ascending?: boolean nullsFirst?: boolean diff --git a/apps/studio/data/table-rows/table-rows-query.ts b/apps/studio/data/table-rows/table-rows-query.ts index 654f2b426f4..cccb503f6d8 100644 --- a/apps/studio/data/table-rows/table-rows-query.ts +++ b/apps/studio/data/table-rows/table-rows-query.ts @@ -60,7 +60,7 @@ export const fetchAllTableRows = async ({ queryChains = queryChains.filter(filter.column, filter.operator, value) }) sorts.forEach((sort) => { - queryChains = queryChains.order(sort.column, sort.ascending, sort.nullsFirst) + queryChains = queryChains.order(sort.table, sort.column, sort.ascending, sort.nullsFirst) }) // Starting from page 0, fetch 500 records per call @@ -134,7 +134,7 @@ export const getTableRowsSqlQuery = ({ queryChains = queryChains.filter(x.column, x.operator, value) }) sorts.forEach((x) => { - queryChains = queryChains.order(x.column, x.ascending, x.nullsFirst) + queryChains = queryChains.order(x.table, x.column, x.ascending, x.nullsFirst) }) // getPagination is expecting to start from 0 diff --git a/apps/studio/tests/components/Grid/Grid.utils.test.ts b/apps/studio/tests/components/Grid/Grid.utils.test.ts index ad20da939a6..a12c9874a3e 100644 --- a/apps/studio/tests/components/Grid/Grid.utils.test.ts +++ b/apps/studio/tests/components/Grid/Grid.utils.test.ts @@ -1,26 +1,21 @@ -import { formatSortURLParams, formatFilterURLParams } from 'components/grid/SupabaseGrid.utils' +import { formatFilterURLParams, formatSortURLParams } from 'components/grid/SupabaseGrid.utils' // Sort URL syntax: `column:order` describe('SupabaseGrid.utils: formatSortURLParams', () => { test('should return an array of sort options based on URL params', () => { const mockInput = ['id:asc', 'name:desc'] - const output = formatSortURLParams(mockInput) + const output = formatSortURLParams('fakeTable', mockInput) expect(output).toStrictEqual([ - { - column: 'id', - ascending: true, - }, - { - column: 'name', - ascending: false, - }, + { table: 'fakeTable', column: 'id', ascending: true }, + { table: 'fakeTable', column: 'name', ascending: false }, ]) }) test('should reject any malformed sort options based on URL params', () => { const mockInput = ['id', 'name:asc', ':asc'] - const output = formatSortURLParams(mockInput) + const output = formatSortURLParams('fakeTable', mockInput) expect(output).toStrictEqual([ { + table: 'fakeTable', column: 'name', ascending: true, },