( table: TableDefinition, data: BulkDeleteData, requestId: string )
| 1655 | * @returns Bulk operation result |
| 1656 | */ |
| 1657 | export async function deleteRowsByFilter( |
| 1658 | table: TableDefinition, |
| 1659 | data: BulkDeleteData, |
| 1660 | requestId: string |
| 1661 | ): Promise<BulkOperationResult> { |
| 1662 | const tableName = USER_TABLE_ROWS_SQL_NAME |
| 1663 | |
| 1664 | // Build filter clause |
| 1665 | const filterClause = buildFilterClause(data.filter, tableName, table.schema.columns) |
| 1666 | if (!filterClause) { |
| 1667 | throw new Error('Filter is required for bulk delete') |
| 1668 | } |
| 1669 | |
| 1670 | // Find matching rows |
| 1671 | const baseConditions = and( |
| 1672 | eq(userTableRows.tableId, table.id), |
| 1673 | eq(userTableRows.workspaceId, table.workspaceId) |
| 1674 | ) |
| 1675 | |
| 1676 | // Tenant-bounded for the same reason as updateRowsByFilter — see withSeqscanOff. |
| 1677 | const matchingRows = await withSeqscanOff(async (trx) => { |
| 1678 | let query = trx |
| 1679 | .select({ id: userTableRows.id, position: userTableRows.position }) |
| 1680 | .from(userTableRows) |
| 1681 | .where(and(baseConditions, filterClause)) |
| 1682 | if (data.limit) { |
| 1683 | query = query.limit(data.limit) as typeof query |
| 1684 | } |
| 1685 | return query |
| 1686 | }) |
| 1687 | |
| 1688 | if (matchingRows.length === 0) { |
| 1689 | return { affectedCount: 0, affectedRowIds: [] } |
| 1690 | } |
| 1691 | |
| 1692 | const rowIds = matchingRows.map((r) => r.id) |
| 1693 | |
| 1694 | await deleteOrderedRowsByIds({ |
| 1695 | tableId: table.id, |
| 1696 | workspaceId: table.workspaceId, |
| 1697 | rowIds, |
| 1698 | }) |
| 1699 | |
| 1700 | logger.info(`[${requestId}] Deleted ${matchingRows.length} rows from table ${table.id}`) |
| 1701 | |
| 1702 | return { |
| 1703 | affectedCount: matchingRows.length, |
| 1704 | affectedRowIds: rowIds, |
| 1705 | } |
| 1706 | } |
| 1707 | |
| 1708 | /** |
| 1709 | * Deletes rows by their IDs. |
no test coverage detected