Skip to content

Commit 7c776a4

Browse files
committed
fix(tables): merge partial updates in updateRow to prevent column data loss
When Mothership called updateRow directly (bypassing the PATCH API route), it passed only the changed fields — which were written as the entire row, wiping all other columns. Move the merge logic into updateRow itself so all callers get correct partial-update semantics, and remove the now-redundant pre-merge from both PATCH routes.
1 parent 0e7f032 commit 7c776a4

3 files changed

Lines changed: 13 additions & 50 deletions

File tree

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

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -135,32 +135,11 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) {
135135
return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 })
136136
}
137137

138-
const [existingRow] = await db
139-
.select({ data: userTableRows.data })
140-
.from(userTableRows)
141-
.where(
142-
and(
143-
eq(userTableRows.id, rowId),
144-
eq(userTableRows.tableId, tableId),
145-
eq(userTableRows.workspaceId, validated.workspaceId)
146-
)
147-
)
148-
.limit(1)
149-
150-
if (!existingRow) {
151-
return NextResponse.json({ error: 'Row not found' }, { status: 404 })
152-
}
153-
154-
const mergedData = {
155-
...(existingRow.data as RowData),
156-
...(validated.data as RowData),
157-
}
158-
159138
const updatedRow = await updateRow(
160139
{
161140
tableId,
162141
rowId,
163-
data: mergedData,
142+
data: validated.data as RowData,
164143
workspaceId: validated.workspaceId,
165144
},
166145
table,

apps/sim/app/api/v1/tables/[tableId]/rows/[rowId]/route.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -137,33 +137,11 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) {
137137
return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 })
138138
}
139139

140-
// Fetch existing row to merge partial update
141-
const [existingRow] = await db
142-
.select({ data: userTableRows.data })
143-
.from(userTableRows)
144-
.where(
145-
and(
146-
eq(userTableRows.id, rowId),
147-
eq(userTableRows.tableId, tableId),
148-
eq(userTableRows.workspaceId, validated.workspaceId)
149-
)
150-
)
151-
.limit(1)
152-
153-
if (!existingRow) {
154-
return NextResponse.json({ error: 'Row not found' }, { status: 404 })
155-
}
156-
157-
const mergedData = {
158-
...(existingRow.data as RowData),
159-
...(validated.data as RowData),
160-
}
161-
162140
const updatedRow = await updateRow(
163141
{
164142
tableId,
165143
rowId,
166-
data: mergedData,
144+
data: validated.data as RowData,
167145
workspaceId: validated.workspaceId,
168146
},
169147
table,

apps/sim/lib/table/service.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,14 +1207,20 @@ export async function updateRow(
12071207
throw new Error('Row not found')
12081208
}
12091209

1210+
// Merge partial update with existing row data so callers can pass only changed fields
1211+
const mergedData = {
1212+
...(existingRow.data as RowData),
1213+
...data.data,
1214+
}
1215+
12101216
// Validate size
1211-
const sizeValidation = validateRowSize(data.data)
1217+
const sizeValidation = validateRowSize(mergedData)
12121218
if (!sizeValidation.valid) {
12131219
throw new Error(sizeValidation.errors.join(', '))
12141220
}
12151221

12161222
// Validate against schema
1217-
const schemaValidation = validateRowAgainstSchema(data.data, table.schema)
1223+
const schemaValidation = validateRowAgainstSchema(mergedData, table.schema)
12181224
if (!schemaValidation.valid) {
12191225
throw new Error(`Schema validation failed: ${schemaValidation.errors.join(', ')}`)
12201226
}
@@ -1224,7 +1230,7 @@ export async function updateRow(
12241230
if (uniqueColumns.length > 0) {
12251231
const uniqueValidation = await checkUniqueConstraintsDb(
12261232
data.tableId,
1227-
data.data,
1233+
mergedData,
12281234
table.schema,
12291235
data.rowId // Exclude current row
12301236
)
@@ -1237,14 +1243,14 @@ export async function updateRow(
12371243

12381244
await db
12391245
.update(userTableRows)
1240-
.set({ data: data.data, updatedAt: now })
1246+
.set({ data: mergedData, updatedAt: now })
12411247
.where(eq(userTableRows.id, data.rowId))
12421248

12431249
logger.info(`[${requestId}] Updated row ${data.rowId} in table ${data.tableId}`)
12441250

12451251
return {
12461252
id: data.rowId,
1247-
data: data.data,
1253+
data: mergedData,
12481254
position: existingRow.position,
12491255
createdAt: existingRow.createdAt,
12501256
updatedAt: now,

0 commit comments

Comments
 (0)