Skip to content

Commit 2e0f506

Browse files
fix(table): preserve workflow groups on CSV column-add and dispatch after tx commit (#4503)
Two bugs in the CSV-import path: - addTableColumnsWithTx rebuilt the schema with only `columns`, dropping `workflowGroups` (and any other top-level schema fields). Importing CSV into a table that has workflow groups erased the group config. Spread `table.schema` first so siblings survive. - batchInsertRowsWithTx fired fireTableTrigger and scheduleRunsForRows from inside the caller's transaction. Both read through the global db connection, so they could run before the inserts committed and see no rows. Extracted the dispatch into dispatchAfterBatchInsert; non-tx wrapper fires it after `db.transaction(...)` resolves, and the CSV import route does the same after its tx.
1 parent e5a46d7 commit 2e0f506

3 files changed

Lines changed: 38 additions & 9 deletions

File tree

apps/sim/app/api/table/[tableId]/import/route.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ const {
1111
mockBatchInsertRowsWithTx,
1212
mockReplaceTableRowsWithTx,
1313
mockAddTableColumnsWithTx,
14+
mockDispatchAfterBatchInsert,
1415
} = vi.hoisted(() => ({
1516
mockCheckAccess: vi.fn(),
1617
mockBatchInsertRowsWithTx: vi.fn(),
1718
mockReplaceTableRowsWithTx: vi.fn(),
1819
mockAddTableColumnsWithTx: vi.fn(),
20+
mockDispatchAfterBatchInsert: vi.fn(),
1921
}))
2022

2123
vi.mock('@sim/utils/id', () => ({
@@ -44,6 +46,7 @@ vi.mock('@/lib/table/service', () => ({
4446
batchInsertRowsWithTx: mockBatchInsertRowsWithTx,
4547
replaceTableRowsWithTx: mockReplaceTableRowsWithTx,
4648
addTableColumnsWithTx: mockAddTableColumnsWithTx,
49+
dispatchAfterBatchInsert: mockDispatchAfterBatchInsert,
4750
}))
4851

4952
import { POST } from '@/app/api/table/[tableId]/import/route'

apps/sim/app/api/table/[tableId]/import/route.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ import {
2929
type CsvHeaderMapping,
3030
CsvImportValidationError,
3131
coerceRowsForTable,
32+
dispatchAfterBatchInsert,
3233
inferColumnType,
3334
parseCsvBuffer,
3435
replaceTableRowsWithTx,
3536
sanitizeName,
3637
type TableDefinition,
38+
type TableRow,
3739
type TableSchema,
3840
validateMapping,
3941
} from '@/lib/table'
@@ -228,13 +230,13 @@ export const POST = withRouteHandler(async (request: NextRequest, { params }: Ro
228230
}
229231

230232
try {
231-
const inserted = await db.transaction(async (trx) => {
233+
const txResult = await db.transaction(async (trx) => {
232234
let working = table
233235
if (additions.length > 0) {
234236
working = await addTableColumnsWithTx(trx, table, additions, requestId)
235237
}
236238

237-
let total = 0
239+
const allInserted: TableRow[] = []
238240
for (let i = 0; i < coerced.length; i += CSV_MAX_BATCH_SIZE) {
239241
const batch = coerced.slice(i, i + CSV_MAX_BATCH_SIZE)
240242
const batchRequestId = generateId().slice(0, 8)
@@ -249,10 +251,15 @@ export const POST = withRouteHandler(async (request: NextRequest, { params }: Ro
249251
working,
250252
batchRequestId
251253
)
252-
total += result.length
254+
allInserted.push(...result)
253255
}
254-
return total
256+
return { inserted: allInserted, working }
255257
})
258+
const { inserted: insertedRows, working: finalTable } = txResult
259+
const inserted = insertedRows.length
260+
// Fire trigger + scheduler AFTER the tx commits — both read through the
261+
// global db connection and would otherwise see no rows.
262+
dispatchAfterBatchInsert(finalTable, insertedRows, requestId)
256263

257264
logger.info(`[${requestId}] Append CSV imported`, {
258265
tableId: table.id,

apps/sim/lib/table/service.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,12 @@ export async function addTableColumnsWithTx(
643643
)
644644
}
645645

646-
const updatedSchema: TableSchema = { columns: [...table.schema.columns, ...additions] }
646+
// Spread `table.schema` first so workflow groups (and any future top-level
647+
// schema fields) survive a CSV import that only adds plain columns.
648+
const updatedSchema: TableSchema = {
649+
...table.schema,
650+
columns: [...table.schema.columns, ...additions],
651+
}
647652
const now = new Date()
648653

649654
await trx
@@ -1067,7 +1072,9 @@ export async function batchInsertRows(
10671072
table: TableDefinition,
10681073
requestId: string
10691074
): Promise<TableRow[]> {
1070-
return db.transaction((trx) => batchInsertRowsWithTx(trx, data, table, requestId))
1075+
const result = await db.transaction((trx) => batchInsertRowsWithTx(trx, data, table, requestId))
1076+
dispatchAfterBatchInsert(table, result, requestId)
1077+
return result
10711078
}
10721079

10731080
/**
@@ -1165,7 +1172,21 @@ export async function batchInsertRowsWithTx(
11651172
updatedAt: r.updatedAt,
11661173
}))
11671174

1168-
void fireTableTrigger(data.tableId, table.name, 'insert', result, null, table.schema, requestId)
1175+
return result
1176+
}
1177+
1178+
/**
1179+
* Side-effect dispatch for an insert batch. Caller fires this AFTER the
1180+
* surrounding transaction commits — `fireTableTrigger` and `runWorkflowColumn`
1181+
* both read through the global db connection, so firing inside the tx can see
1182+
* no rows and no-op.
1183+
*/
1184+
export function dispatchAfterBatchInsert(
1185+
table: TableDefinition,
1186+
result: TableRow[],
1187+
requestId: string
1188+
): void {
1189+
void fireTableTrigger(table.id, table.name, 'insert', result, null, table.schema, requestId)
11691190
// Scope to the newly-inserted row ids so the dispatcher doesn't walk every
11701191
// row in the table. After the sidecar migration, all existing rows have
11711192
// zero entries → `mode:'new'`'s `NOT EXISTS` filter would otherwise include
@@ -1178,8 +1199,6 @@ export async function batchInsertRowsWithTx(
11781199
isManualRun: false,
11791200
requestId,
11801201
}).catch((err) => logger.error(`[${requestId}] auto-dispatch (batchInsertRows) failed:`, err))
1181-
1182-
return result
11831202
}
11841203

11851204
/**

0 commit comments

Comments
 (0)