(
data: { tableId: string; columnNames: string[] },
requestId: string
)
| 365 | * Avoids the race condition of calling deleteColumn multiple times in parallel. |
| 366 | */ |
| 367 | export async function deleteColumns( |
| 368 | data: { tableId: string; columnNames: string[] }, |
| 369 | requestId: string |
| 370 | ): Promise<TableDefinition> { |
| 371 | const { def, stripKeys } = await withLockedTable(data.tableId, async (table, trx) => { |
| 372 | const schema = table.schema |
| 373 | const namesToDelete = new Set<string>() |
| 374 | const idsToDelete = new Set<string>() |
| 375 | const notFound: string[] = [] |
| 376 | |
| 377 | for (const name of data.columnNames) { |
| 378 | const col = schema.columns.find((c) => columnMatchesRef(c, name)) |
| 379 | if (!col) { |
| 380 | notFound.push(name) |
| 381 | } else { |
| 382 | namesToDelete.add(col.name) |
| 383 | idsToDelete.add(getColumnId(col)) |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | if (notFound.length > 0) { |
| 388 | throw new Error(`Columns not found: ${notFound.join(', ')}`) |
| 389 | } |
| 390 | |
| 391 | const remaining = schema.columns.filter((c) => !namesToDelete.has(c.name)) |
| 392 | if (remaining.length === 0) { |
| 393 | throw new Error('Cannot delete all columns from a table') |
| 394 | } |
| 395 | |
| 396 | // For each group, drop outputs whose column (by id) is being deleted. Groups |
| 397 | // that end up with zero outputs are removed entirely (they'd be invalid). |
| 398 | // Then any remaining group's dependencies referencing a removed column are |
| 399 | // cleaned up. |
| 400 | const removedGroupIds = new Set<string>() |
| 401 | let updatedGroups = (schema.workflowGroups ?? []).map((group) => { |
| 402 | const remainingOutputs = group.outputs.filter((o) => !idsToDelete.has(o.columnName)) |
| 403 | if (remainingOutputs.length === 0) { |
| 404 | removedGroupIds.add(group.id) |
| 405 | } |
| 406 | return remainingOutputs.length === group.outputs.length |
| 407 | ? group |
| 408 | : { ...group, outputs: remainingOutputs } |
| 409 | }) |
| 410 | updatedGroups = updatedGroups |
| 411 | .filter((g) => !removedGroupIds.has(g.id)) |
| 412 | .map((group) => stripGroupDeps(group, idsToDelete)) |
| 413 | const updatedSchema: TableSchema = { |
| 414 | ...schema, |
| 415 | columns: remaining, |
| 416 | ...(updatedGroups.length > 0 ? { workflowGroups: updatedGroups } : {}), |
| 417 | } |
| 418 | const updatedMetadata = stripColumnIdsFromMetadata( |
| 419 | table.metadata as TableMetadata | null, |
| 420 | idsToDelete |
| 421 | ) |
| 422 | assertValidSchema(updatedSchema, updatedMetadata?.columnOrder) |
| 423 | |
| 424 | const now = new Date() |
no test coverage detected