fix: paste seq sync (#45116)

## TL;DR
- extends https://github.com/supabase/supabase/pull/45076
- closes https://github.com/supabase/supabase/issues/45113


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Sequence values now properly sync after bulk row inserts, ensuring
correct auto-increment behavior for subsequent inserts.

* **Tests**
* Added end-to-end coverage for CSV import, including a "Paste text"
import path that verifies custom-owned sequences are synchronized before
the next insert.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
This commit is contained in:
Vaibhav
2026-04-22 20:04:12 +05:30
committed by GitHub
parent 1d6f883622
commit d9b58fdf3e
2 changed files with 120 additions and 3 deletions
@@ -1163,11 +1163,49 @@ export async function insertTableRows({
const batchedPromises = chunk(tasks, 10)
for (const batchedPromise of batchedPromises) {
const res = await Promise.allSettled(batchedPromise.map((batch) => batch()))
const hasFailedBatch = find(res, { status: 'rejected' })
if (hasFailedBatch) break
const failedBatch = res.find((result) => result.status === 'rejected')
if (failedBatch?.status === 'rejected') {
if (insertError === undefined) insertError = failedBatch.reason
break
}
onProgressUpdate(insertProgress * 100)
}
return { error: insertError }
if (insertError !== undefined) {
return { error: insertError }
}
const sequenceColumns = (table.columns ?? []).filter(
(column) =>
column.is_identity ||
(typeof column.default_value === 'string' && column.default_value.includes('nextval('))
)
if (sequenceColumns.length === 0) {
return { error: insertError }
}
const updateSequenceSQL = sequenceColumns
.map((column) =>
getUpdateIdentitySequenceSQL({
schema: table.schema,
table: table.name,
column: column.name,
})
)
.join(';\n')
try {
await executeSql({
projectRef,
connectionString,
sql: updateSequenceSQL,
queryKey: ['sequences', 'update-batch'],
})
return { error: insertError }
} catch (error) {
return { error }
}
}
const updateForeignKeys = async ({
+79
View File
@@ -1369,6 +1369,85 @@ testRunner('table editor', () => {
.toBe('230')
})
test('pasted CSV text syncs custom owned sequences before the next insert', async ({
page,
ref,
}) => {
const tableName = 'pw_table_paste_sequence_sync'
const sequenceName = 'pw_table_paste_import_owned_seq'
await using _ = await withSetupCleanup(
async () => {
await query(`drop table if exists public.${tableName} cascade;`)
await query(`drop sequence if exists public.${sequenceName};`)
await query(`create sequence public.${sequenceName};`)
await query(`create table public.${tableName} (
id bigint primary key default nextval('public.${sequenceName}'),
name text
);`)
await query(`alter sequence public.${sequenceName} owned by public.${tableName}.id;`)
},
async () => {
await query(`drop table if exists public.${tableName} cascade;`)
await query(`drop sequence if exists public.${sequenceName};`)
}
)
const waitForTable = waitForTableToLoad(page, ref)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTable
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
const csvFilePath = path.join(import.meta.dirname, 'files', 'table-editor-import-sequence.csv')
const csvText = fs.readFileSync(csvFilePath, 'utf-8')
await page.getByRole('button', { name: 'Import data from CSV' }).click()
await page.getByRole('tab', { name: 'Paste text' }).click()
await page.getByRole('textbox').fill(csvText)
await expect(page.getByText('A total of 3 rows will be')).toBeVisible()
const waitForCsvInsert = createApiResponseWaiter(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
})
await page.getByRole('button', { name: 'Import data' }).click()
await waitForCsvInsert
await waitForGridDataToLoad(page, ref)
await expect(page.getByText('3 records')).toBeVisible()
await expect
.poll(async () => {
const [{ state }] = await query<{ state: string }>(`
select format(
'%s|%s|%s',
(select coalesce(max(id), 0) from public.${tableName}),
last_value,
is_called
) as state
from public.${sequenceName};
`)
return state
})
.toBe('229|229|t')
await page.getByTestId('table-editor-insert-new-row').click()
await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
await page.getByTestId('name-input').fill('Dave')
const insertPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
})
await page.getByTestId('action-bar-save-row').click()
await insertPromise
await expect
.poll(async () => {
const [{ id }] = await query<{ id: string }>(
`select id::text as id from public.${tableName} where name = 'Dave'`
)
return id
})
.toBe('230')
})
test('row insert via side panel saves immediately', async ({ page, ref }) => {
const tableName = 'pw_table_row_insert'
const columnName = 'name'