Skip to content

Commit 8bba1cf

Browse files
authored
Merge e3a55c5 into 9fddd5c
2 parents 9fddd5c + e3a55c5 commit 8bba1cf

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

packages/firestore/src/core/transaction.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,18 @@ export class Transaction {
142142

143143
/**
144144
* Returns the version of this document when it was read in this transaction,
145-
* as a precondition, or no precondition if it was not read.
145+
* as a precondition, or an existence precondition if the document did not
146+
* exist, or no precondition if it was not read.
146147
*/
147148
private precondition(key: DocumentKey): Precondition {
148149
const version = this.readVersions.get(key.toString());
149150
if (!this.writtenDocs.has(key.toString()) && version) {
150-
return Precondition.updateTime(version);
151+
if (version.isEqual(SnapshotVersion.min())) {
152+
// The document doesn't exist.
153+
return Precondition.exists(false);
154+
} else {
155+
return Precondition.updateTime(version);
156+
}
151157
} else {
152158
return Precondition.none();
153159
}

packages/firestore/test/integration/api/transactions.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
Firestore,
3030
FirestoreError,
3131
getDoc,
32+
deleteDoc,
3233
runTransaction,
3334
setDoc
3435
} from '../util/firebase_export';
@@ -610,6 +611,57 @@ apiDescribe('Database transactions', (persistence: boolean) => {
610611
});
611612
});
612613

614+
it('can set and delete inside a transaction on a deleted document', () => {
615+
return withTestDb(persistence, db => {
616+
const docRef = doc(collection(db, 'foo'));
617+
618+
// A function that reads a document then sets some data for it inside the
619+
// context of the given transaction.
620+
const readAndSet: (txn: Transaction) => Promise<Transaction> = (
621+
txn: Transaction
622+
) => txn.get(docRef).then(() => txn.set(docRef, { count: 16 }));
623+
624+
// A function that reads a document then deletes it inside the context of
625+
// the given transaction.
626+
const readAndDelete: (txn: Transaction) => Promise<Transaction> = (
627+
txn: Transaction
628+
) => txn.get(docRef).then(() => txn.delete(docRef));
629+
630+
return (
631+
// Perform a readAndSet, then delete the document, then perform readAndSet
632+
// again. This sequence of operations is valid and should succeed.
633+
runTransaction(db, txn => readAndSet(txn))
634+
.then(() => getDoc(docRef))
635+
.then(snapshot => {
636+
expect(snapshot.exists()).to.equal(true);
637+
expect(snapshot.data()).to.deep.equal({ count: 16 });
638+
})
639+
.then(() => deleteDoc(docRef))
640+
.then(() => getDoc(docRef))
641+
.then(snapshot => expect(snapshot.exists()).to.equal(false))
642+
// Perform a transaction to read and set for the second time.
643+
.then(() => runTransaction(db, txn => readAndSet(txn)))
644+
.then(() => getDoc(docRef))
645+
.then(snapshot => {
646+
expect(snapshot.exists()).to.equal(true);
647+
expect(snapshot.data()).to.deep.equal({ count: 16 });
648+
})
649+
// Perform readAndDelete twice. This is also valid and should succeed.
650+
.then(() => runTransaction(db, txn => readAndDelete(txn)))
651+
.then(() => getDoc(docRef))
652+
.then(snapshot => expect(snapshot.exists()).to.equal(false))
653+
.then(() => runTransaction(db, txn => readAndDelete(txn)))
654+
.then(() => getDoc(docRef))
655+
.then(snapshot => {
656+
expect(snapshot.exists()).to.equal(false);
657+
})
658+
.catch((err: FirestoreError) => {
659+
expect.fail('Expected the transaction to succeed, but got ' + err);
660+
})
661+
);
662+
});
663+
});
664+
613665
// PORTING NOTE: These tests are for FirestoreDataConverter support and apply
614666
// only to web.
615667
apiDescribe('withConverter() support', (persistence: boolean) => {

0 commit comments

Comments
 (0)