diff --git a/src/indexer/db.ts b/src/indexer/db.ts index a0f6057..6503b4f 100644 --- a/src/indexer/db.ts +++ b/src/indexer/db.ts @@ -1149,6 +1149,47 @@ export async function insertEventBatchLocked( }); } +/** + * Insert a batch of events WITHOUT touching the live indexer_state ledger + * pointer. Used for custom historical event imports (event_type_filter's + * dynamic start/end ledger support) so a backfill over an arbitrary past + * range can never advance or rewind last_ledger_sequence - only the live + * poller (insertEventBatch, driven strictly by lastLedger+1..currentLedger) + * is allowed to move that pointer. Rows still go through INSERT OR IGNORE + * against the same UNIQUE(contract_id, ledger_sequence, event_type) + * constraint, so re-running a historical import is idempotent exactly like + * the live poller. + * + * Returns the number of rows actually inserted (excludes rows ignored as + * duplicates). + */ +export function insertHistoricalEventBatch(events: EventRow[]): number { + const db = getDb(); + + const insertStmt = db.prepare(` + INSERT OR IGNORE INTO events + (contract_id, event_type, ledger_sequence, timestamp, data_json) + VALUES (?, ?, ?, ?, ?) + `); + + const batchTransaction = db.transaction(() => { + let inserted = 0; + for (const ev of events) { + const result = insertStmt.run( + ev.contractId, + ev.eventType, + ev.ledgerSequence, + ev.timestamp, + ev.dataJson + ); + if (result.changes > 0) inserted++; + } + return inserted; + }); + + return batchTransaction(); +} + // --------------------------------------------------------------------------- // Event queries // ---------------------------------------------------------------------------