Skip to content

Commit 5f474f1

Browse files
KyleAMathewsclaude
andauthored
Fix validating bulk insert assigning same ID to all items (#929)
fix(db): detect duplicate keys within same bulk insert batch Previously, when inserting multiple items with the same key in a single bulk insert operation, later items would silently overwrite earlier ones. This fix adds tracking of keys already processed within the current batch, throwing a DuplicateKeyError when duplicate keys are detected. Co-authored-by: Claude <[email protected]>
1 parent 8f389a1 commit 5f474f1

3 files changed

Lines changed: 54 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tanstack/db": patch
3+
---
4+
5+
Fix bulk insert not detecting duplicate keys within the same batch. Previously, when inserting multiple items with the same key in a single bulk insert operation, later items would silently overwrite earlier ones. Now, a `DuplicateKeyError` is thrown when duplicate keys are detected within the same batch.

packages/db/src/collection/mutations.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,17 +163,19 @@ export class CollectionMutationsManager<
163163

164164
const items = Array.isArray(data) ? data : [data]
165165
const mutations: Array<PendingMutation<TOutput>> = []
166+
const keysInCurrentBatch = new Set<TKey>()
166167

167168
// Create mutations for each item
168169
items.forEach((item) => {
169170
// Validate the data against the schema if one exists
170171
const validatedData = this.validateData(item, `insert`)
171172

172-
// Check if an item with this ID already exists in the collection
173+
// Check if an item with this ID already exists in the collection or in the current batch
173174
const key = this.config.getKey(validatedData)
174-
if (this.state.has(key)) {
175+
if (this.state.has(key) || keysInCurrentBatch.has(key)) {
175176
throw new DuplicateKeyError(key)
176177
}
178+
keysInCurrentBatch.add(key)
177179
const globalKey = this.generateGlobalKey(key, item)
178180

179181
const mutation: PendingMutation<TOutput, `insert`> = {

packages/db/tests/collection.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,51 @@ describe(`Collection`, () => {
567567
}).not.toThrow()
568568
})
569569

570+
it(`should not allow bulk inserting documents with duplicate IDs in the same batch`, async () => {
571+
const collection = createCollection<{ id: number; value: string }>({
572+
id: `bulk-duplicate-id-test`,
573+
getKey: (item) => item.id,
574+
startSync: true,
575+
sync: {
576+
sync: ({ begin, commit, markReady }) => {
577+
begin()
578+
commit()
579+
markReady()
580+
},
581+
},
582+
})
583+
584+
await collection.stateWhenReady()
585+
586+
const mutationFn = async () => {}
587+
const tx = createTransaction({ mutationFn })
588+
589+
// Try to bulk insert documents with duplicate IDs within the same batch
590+
expect(() => {
591+
tx.mutate(() =>
592+
collection.insert([
593+
{ id: 1, value: `first` },
594+
{ id: 1, value: `second` }, // Same ID - should throw
595+
])
596+
)
597+
}).toThrow(DuplicateKeyError)
598+
599+
// Should be able to bulk insert documents with different IDs
600+
const tx2 = createTransaction({ mutationFn })
601+
expect(() => {
602+
tx2.mutate(() =>
603+
collection.insert([
604+
{ id: 2, value: `first` },
605+
{ id: 3, value: `second` },
606+
])
607+
)
608+
}).not.toThrow()
609+
610+
// Verify both items were inserted
611+
expect(collection.state.get(2)).toEqual({ id: 2, value: `first` })
612+
expect(collection.state.get(3)).toEqual({ id: 3, value: `second` })
613+
})
614+
570615
it(`should support operation handler functions`, async () => {
571616
// Create mock handler functions
572617
const onInsertMock = vi.fn()

0 commit comments

Comments
 (0)