Skip to content

Commit 8abe717

Browse files
committed
Fix table column delete
1 parent d815568 commit 8abe717

File tree

3 files changed

+87
-5
lines changed

3 files changed

+87
-5
lines changed

apps/sim/lib/copilot/tools/server/table/user-table.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
batchInsertRows,
77
createTable,
88
deleteColumn,
9+
deleteColumns,
910
deleteRow,
1011
deleteRowsByFilter,
1112
deleteTable,
@@ -695,17 +696,30 @@ export const userTableServerTool: BaseServerTool<UserTableArgs, UserTableResult>
695696
return { success: false, message: 'Table ID is required' }
696697
}
697698
const colName = (args as Record<string, unknown>).columnName as string | undefined
698-
if (!colName) {
699-
return { success: false, message: 'columnName is required' }
699+
const colNames = (args as Record<string, unknown>).columnNames as string[] | undefined
700+
const names = colNames ?? (colName ? [colName] : null)
701+
if (!names || names.length === 0) {
702+
return { success: false, message: 'columnName or columnNames is required' }
700703
}
701704
const requestId = crypto.randomUUID().slice(0, 8)
702-
const updated = await deleteColumn(
703-
{ tableId: args.tableId, columnName: colName },
705+
if (names.length === 1) {
706+
const updated = await deleteColumn(
707+
{ tableId: args.tableId, columnName: names[0] },
708+
requestId
709+
)
710+
return {
711+
success: true,
712+
message: `Deleted column "${names[0]}"`,
713+
data: { schema: updated.schema },
714+
}
715+
}
716+
const updated = await deleteColumns(
717+
{ tableId: args.tableId, columnNames: names },
704718
requestId
705719
)
706720
return {
707721
success: true,
708-
message: `Deleted column "${colName}"`,
722+
message: `Deleted ${names.length} columns: ${names.join(', ')}`,
709723
data: { schema: updated.schema },
710724
}
711725
}

apps/sim/lib/copilot/tools/shared/schemas.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export const UserTableArgsSchema = z.object({
149149
})
150150
.optional(),
151151
columnName: z.string().optional(),
152+
columnNames: z.array(z.string()).optional(),
152153
newName: z.string().optional(),
153154
newType: z.string().optional(),
154155
unique: z.boolean().optional(),

apps/sim/lib/table/service.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,73 @@ export async function deleteColumn(
13681368
return { ...table, schema: updatedSchema, metadata: updatedMetadata, updatedAt: now }
13691369
}
13701370

1371+
/**
1372+
* Deletes multiple columns from a table in a single transaction.
1373+
* Avoids the race condition of calling deleteColumn multiple times in parallel.
1374+
*/
1375+
export async function deleteColumns(
1376+
data: { tableId: string; columnNames: string[] },
1377+
requestId: string
1378+
): Promise<TableDefinition> {
1379+
const table = await getTableById(data.tableId)
1380+
if (!table) {
1381+
throw new Error('Table not found')
1382+
}
1383+
1384+
const schema = table.schema
1385+
const namesToDelete = new Set<string>()
1386+
const notFound: string[] = []
1387+
1388+
for (const name of data.columnNames) {
1389+
const col = schema.columns.find((c) => c.name.toLowerCase() === name.toLowerCase())
1390+
if (!col) {
1391+
notFound.push(name)
1392+
} else {
1393+
namesToDelete.add(col.name)
1394+
}
1395+
}
1396+
1397+
if (notFound.length > 0) {
1398+
throw new Error(`Columns not found: ${notFound.join(', ')}`)
1399+
}
1400+
1401+
const remaining = schema.columns.filter((c) => !namesToDelete.has(c.name))
1402+
if (remaining.length === 0) {
1403+
throw new Error('Cannot delete all columns from a table')
1404+
}
1405+
1406+
const updatedSchema: TableSchema = { columns: remaining }
1407+
1408+
const metadata = table.metadata as TableMetadata | null
1409+
let updatedMetadata = metadata
1410+
if (metadata?.columnWidths) {
1411+
const widths = { ...metadata.columnWidths }
1412+
for (const n of namesToDelete) delete widths[n]
1413+
updatedMetadata = { ...metadata, columnWidths: widths }
1414+
}
1415+
1416+
const now = new Date()
1417+
1418+
await db.transaction(async (trx) => {
1419+
await trx
1420+
.update(userTableDefinitions)
1421+
.set({ schema: updatedSchema, metadata: updatedMetadata, updatedAt: now })
1422+
.where(eq(userTableDefinitions.id, data.tableId))
1423+
1424+
for (const name of namesToDelete) {
1425+
await trx.execute(
1426+
sql`UPDATE user_table_rows SET data = data - ${name} WHERE table_id = ${data.tableId} AND data ? ${name}`
1427+
)
1428+
}
1429+
})
1430+
1431+
logger.info(
1432+
`[${requestId}] Deleted columns [${[...namesToDelete].join(', ')}] from table ${data.tableId}`
1433+
)
1434+
1435+
return { ...table, schema: updatedSchema, metadata: updatedMetadata, updatedAt: now }
1436+
}
1437+
13711438
/**
13721439
* Changes the type of a column. Validates that existing data is compatible.
13731440
*

0 commit comments

Comments
 (0)