diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc5370e3d7..4b8dd409d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - FIX findByIds not working with modify and patch [#6592](https://github.com/pubkey/rxdb/pull/6592) - +- FIX bulk inserting 2 docs with same primary key should throw [#6589](https://github.com/pubkey/rxdb/pull/6589) diff --git a/src/plugins/dev-mode/error-messages.ts b/src/plugins/dev-mode/error-messages.ts index 41cefc99429..4b705b3eeb1 100644 --- a/src/plugins/dev-mode/error-messages.ts +++ b/src/plugins/dev-mode/error-messages.ts @@ -100,6 +100,7 @@ export const ERROR_MESSAGES = { COL20: 'Storage write error', COL21: 'The RxCollection is destroyed or removed already, either from this JavaScript realm or from another, like a browser tab', CONFLICT: 'Document update conflict. When changing a document you must work on the previous revision', + COL22: '.bulkInsert() and .bulkUpsert() cannot be run with multiple documents that have the same primary key', // rx-document.js DOC1: 'RxDocument.get$ cannot get observable of in-array fields because order cannot be guessed', diff --git a/src/rx-collection.ts b/src/rx-collection.ts index 8043116f67a..38bd326e14c 100644 --- a/src/rx-collection.ts +++ b/src/rx-collection.ts @@ -376,6 +376,7 @@ export class RxCollectionBase< const primaryPath = this.schema.primaryPath; + const ids = new Set(); /** * This code is a bit redundant for better performance. @@ -386,6 +387,7 @@ export class RxCollectionBase< if (this.hasHooks('pre', 'insert')) { insertRows = await Promise.all( docsData.map(docData => { + ids.add((docData as any)[primaryPath]); const useDocData = fillObjectDataBeforeInsert(this.schema, docData); return this._runHooks('pre', 'insert', useDocData) .then(() => { @@ -398,11 +400,22 @@ export class RxCollectionBase< const schema = this.schema; for (let index = 0; index < docsData.length; index++) { const docData = docsData[index]; + ids.add((docData as any)[primaryPath]); const useDocData = fillObjectDataBeforeInsert(schema, docData); insertRows[index] = { document: useDocData }; } } + + if (ids.size !== docsData.length) { + throw newRxError('COL22', { + collection: this.name, + args: { + documents: docsData + } + }); + } + const results = await this.storageInstance.bulkWrite( insertRows, 'rx-collection-bulk-insert' diff --git a/test/unit/rx-collection.test.ts b/test/unit/rx-collection.test.ts index 0eec38dafeb..974e18387d9 100644 --- a/test/unit/rx-collection.test.ts +++ b/test/unit/rx-collection.test.ts @@ -389,6 +389,29 @@ describe('rx-collection.test.ts', () => { assert.strictEqual(ret.error.length, 1); db.destroy(); }); + /** + * @link https://github.com/pubkey/rxdb/pull/6589 + */ + it('should throw on duplicate ids', async () => { + const db = await createRxDatabase({ + name: randomCouchString(10), + storage: config.storage.getStorage(), + }); + const collections = await db.addCollections({ + human: { schema: schemas.primaryHuman } + }); + + const human1 = schemaObjects.humanData('same-id'); + const human2 = schemaObjects.humanData('same-id'); + + await assertThrows( + () => collections.human.bulkInsert([human1, human2]), + 'RxError', + 'COL22' + ); + + db.destroy(); + }); }); }); describe('.find()', () => { @@ -1528,6 +1551,39 @@ describe('rx-collection.test.ts', () => { c.database.destroy(); }); + /** + * @link https://github.com/pubkey/rxdb/pull/6589 + */ + it('should throw on duplicate ids', async () => { + const db = await createRxDatabase({ + name: randomCouchString(10), + storage: config.storage.getStorage(), + }); + const collections = await db.addCollections({ + human: { schema: schemas.primaryHuman } + }); + + const human1 = schemaObjects.humanData('same-id'); + const human2 = schemaObjects.humanData('same-id'); + + await assertThrows( + () => collections.human.bulkUpsert([human1, human2]), + 'RxError', + 'COL22' + ); + + // should be the same behavior even if the document already exists + await collections.human.insert(human1); + await assertThrows( + () => collections.human.bulkUpsert([human1, human2]), + 'RxError', + 'COL22' + ); + + + + db.destroy(); + }); }); describeParallel('.upsert()', () => { describe('positive', () => {