( db: Awaited<ReturnType<typeof getDB>>, tableName: string, pk: string, timestampCol: string, ids: unknown[], )
| 494 | } |
| 495 | |
| 496 | async function loadExistingRecordStates( |
| 497 | db: Awaited<ReturnType<typeof getDB>>, |
| 498 | tableName: string, |
| 499 | pk: string, |
| 500 | timestampCol: string, |
| 501 | ids: unknown[], |
| 502 | ): Promise<Map<string, ExistingRecordState>> { |
| 503 | const states = new Map<string, ExistingRecordState>(); |
| 504 | if (ids.length === 0) return states; |
| 505 | |
| 506 | const columns = await getTableColumns(db, tableName); |
| 507 | const deletedAtSelect = columns.has("deleted_at") ? ", deleted_at AS deleted_at" : ""; |
| 508 | |
| 509 | const chunkSize = 200; |
| 510 | for (let offset = 0; offset < ids.length; offset += chunkSize) { |
| 511 | const chunk = ids.slice(offset, offset + chunkSize); |
| 512 | const placeholders = chunk.map(() => "?").join(", "); |
| 513 | const rows = await db.select<{ |
| 514 | id: string; |
| 515 | timestamp: number | null; |
| 516 | deleted_at?: number | null; |
| 517 | }>( |
| 518 | `SELECT ${pk} AS id, ${timestampCol} AS timestamp${deletedAtSelect} FROM ${tableName} WHERE ${pk} IN (${placeholders})`, |
| 519 | chunk, |
| 520 | ); |
| 521 | |
| 522 | for (const row of rows) { |
| 523 | states.set(String(row.id), { |
| 524 | timestamp: row.timestamp ?? 0, |
| 525 | deletedAt: normalizeDeletedAt(row.deleted_at), |
| 526 | }); |
| 527 | } |
| 528 | } |
| 529 | |
| 530 | try { |
| 531 | for (let offset = 0; offset < ids.length; offset += chunkSize) { |
| 532 | const chunk = ids.slice(offset, offset + chunkSize); |
| 533 | const placeholders = chunk.map(() => "?").join(", "); |
| 534 | const tombstones = await db.select<{ id: string; deleted_at: number }>( |
| 535 | `SELECT id, deleted_at |
| 536 | FROM sync_tombstones |
| 537 | WHERE table_name = ? |
| 538 | AND id IN (${placeholders})`, |
| 539 | [tableName, ...chunk], |
| 540 | ); |
| 541 | for (const tombstone of tombstones) { |
| 542 | const id = String(tombstone.id); |
| 543 | const deletedAt = tombstone.deleted_at ?? 0; |
| 544 | const existing = states.get(id); |
| 545 | if (!existing || deletedAt > existing.timestamp) { |
| 546 | states.set(id, { |
| 547 | timestamp: deletedAt, |
| 548 | }); |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | } catch { |
| 553 | // sync_tombstones may not exist on older schema variants. |
no test coverage detected