mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 18:11:51 +08:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Bug fix ## What is the current behavior? Fixes #47318 Supabase Studio's batched table edit queue has a few related row identity issues: - Editing a row's primary key can make later queued edits or deletes lose track of the original row. - Editing a primary key and another column in the same row before saving can save only the primary key change, because later updates still use the old primary key in the `WHERE` clause. - Adding a row in batched edit mode and then deleting it before saving may not remove the pending row correctly. ## What is the new behavior? - Preserves the original row identity for queued operations after primary key edits. - Applies multiple queued edits for the same row as a single update when saving. - Correctly deletes newly added pending rows before they are saved. - Adds regression coverage for these batched table edit cases. ## Additional context https://github.com/user-attachments/assets/75672361-d781-4fe5-a542-071574ad57bd <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved row identity handling for grid edits, optimistic updates, and queued operations so changes stay correctly attached when primary keys are edited, reverted, or “taken” by another row. * Updated header row deletion to delete from the currently visible/targeted rows rather than relying on the full dataset. * Reduced retry noise for missing tables by clearing conflicting sorts and preventing repeated retries for the same “does not exist” error. * More reliably consolidated queued edits for the same row into fewer combined save statements. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ali Waseem <waseema393@gmail.com>
66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { getOperationSqlStatements } from './operation-queue-save-mutation'
|
|
import type { Entity } from '@/data/table-editor/table-editor-types'
|
|
import { QueuedOperation, QueuedOperationType } from '@/state/table-editor-operation-queue.types'
|
|
|
|
const usersTable = { id: 1, name: 'users', schema: 'public' } as Entity
|
|
|
|
function createEditOperation(
|
|
columnName: string,
|
|
newValue: unknown,
|
|
rowIdentifiers: Record<string, unknown> = { id: 1 }
|
|
): QueuedOperation {
|
|
return {
|
|
id: `${columnName}-${String(newValue)}`,
|
|
tableId: 1,
|
|
timestamp: 1,
|
|
type: QueuedOperationType.EDIT_CELL_CONTENT,
|
|
payload: {
|
|
rowIdentifiers,
|
|
columnName,
|
|
oldValue: undefined,
|
|
newValue,
|
|
table: usersTable,
|
|
enumArrayColumns: [],
|
|
},
|
|
}
|
|
}
|
|
|
|
describe('getOperationSqlStatements', () => {
|
|
it('merges edits for the same row into one update statement', () => {
|
|
const statements = getOperationSqlStatements([
|
|
createEditOperation('id', '4'),
|
|
createEditOperation('name', 'Ram 1'),
|
|
])
|
|
|
|
expect(statements).toHaveLength(1)
|
|
|
|
const sql = String(statements[0])
|
|
expect(sql).toContain('update public.users set (id,name)')
|
|
expect(sql).toContain('"id":"4"')
|
|
expect(sql).toContain('"name":"Ram 1"')
|
|
expect(sql.match(/where id = 1/g)).toHaveLength(1)
|
|
})
|
|
|
|
it('keeps edits for different rows as separate update statements', () => {
|
|
const statements = getOperationSqlStatements([
|
|
createEditOperation('name', 'Ram 1', { id: 1 }),
|
|
createEditOperation('name', 'Shyam 1', { id: 2 }),
|
|
])
|
|
|
|
expect(statements).toHaveLength(2)
|
|
expect(String(statements[0])).toContain('where id = 1')
|
|
expect(String(statements[1])).toContain('where id = 2')
|
|
})
|
|
|
|
it('does not merge rows whose identifier values would collide with delimiter keys', () => {
|
|
const statements = getOperationSqlStatements([
|
|
createEditOperation('name', 'Row 1', { a: 'x', b: 'y|b:z' }),
|
|
createEditOperation('name', 'Row 2', { a: 'x|b:y', b: 'z' }),
|
|
])
|
|
|
|
expect(statements).toHaveLength(2)
|
|
})
|
|
})
|