( data: UpdateRowData, table: TableDefinition, requestId: string )
| 1116 | * @throws Error if row not found or validation fails |
| 1117 | */ |
| 1118 | export async function updateRow( |
| 1119 | data: UpdateRowData, |
| 1120 | table: TableDefinition, |
| 1121 | requestId: string |
| 1122 | ): Promise<TableRow | null> { |
| 1123 | // Get existing row |
| 1124 | const existingRow = await getRowById(data.tableId, data.rowId, data.workspaceId) |
| 1125 | if (!existingRow) { |
| 1126 | throw new Error('Row not found') |
| 1127 | } |
| 1128 | |
| 1129 | // Merge partial update with existing row data so callers can pass only changed fields |
| 1130 | const mergedData = { |
| 1131 | ...(existingRow.data as RowData), |
| 1132 | ...data.data, |
| 1133 | } |
| 1134 | // Auto-clear exec records for workflow output columns the user just wiped |
| 1135 | // AND for downstream groups whose deps just changed. Surfaces the in-flight |
| 1136 | // downstream groups so the caller can cancel + re-run them. |
| 1137 | const { executionsPatch: effectiveExecutionsPatch, inFlightDownstreamGroups } = |
| 1138 | deriveExecClearsForDataPatch( |
| 1139 | data.data, |
| 1140 | table.schema, |
| 1141 | existingRow.executions, |
| 1142 | data.executionsPatch, |
| 1143 | mergedData |
| 1144 | ) |
| 1145 | const mergedExecutions = applyExecutionsPatch(existingRow.executions, effectiveExecutionsPatch) |
| 1146 | |
| 1147 | // Validate size |
| 1148 | const sizeValidation = validateRowSize(mergedData) |
| 1149 | if (!sizeValidation.valid) { |
| 1150 | throw new Error(sizeValidation.errors.join(', ')) |
| 1151 | } |
| 1152 | |
| 1153 | // Validate against schema |
| 1154 | const schemaValidation = coerceRowToSchema(mergedData, table.schema) |
| 1155 | if (!schemaValidation.valid) { |
| 1156 | throw new Error(`Schema validation failed: ${schemaValidation.errors.join(', ')}`) |
| 1157 | } |
| 1158 | |
| 1159 | // Check unique constraints using optimized database query |
| 1160 | const uniqueColumns = getUniqueColumns(table.schema) |
| 1161 | if (uniqueColumns.length > 0) { |
| 1162 | const uniqueValidation = await checkUniqueConstraintsDb( |
| 1163 | data.tableId, |
| 1164 | mergedData, |
| 1165 | table.schema, |
| 1166 | data.rowId // Exclude current row |
| 1167 | ) |
| 1168 | if (!uniqueValidation.valid) { |
| 1169 | throw new Error(uniqueValidation.errors.join(', ')) |
| 1170 | } |
| 1171 | } |
| 1172 | |
| 1173 | const now = new Date() |
| 1174 | |
| 1175 | // Cell-task partial writes pass `cancellationGuard` so the upsert into |
no test coverage detected