@@ -61,6 +61,14 @@ public class Transaction {
61
61
*/
62
62
private FirebaseFirestoreException lastWriteError ;
63
63
64
+ /**
65
+ * Set of documents that have been written in the transaction.
66
+ *
67
+ * When there's more than one write to the same key in a transaction, any
68
+ * writes after the first are handled differently.
69
+ */
70
+ private Set <DocumentKey > writtenDocs = new HashSet <>();
71
+
64
72
public Transaction (Datastore d ) {
65
73
datastore = d ;
66
74
}
@@ -95,6 +103,7 @@ public Task<List<MaybeDocument>> lookup(List<DocumentKey> keys) {
95
103
/** Stores a set mutation for the given key and value, to be committed when commit() is called. */
96
104
public void set (DocumentKey key , ParsedSetData data ) {
97
105
write (data .toMutationList (key , precondition (key )));
106
+ writtenDocs .add (key );
98
107
}
99
108
100
109
/**
@@ -107,13 +116,12 @@ public void update(DocumentKey key, ParsedUpdateData data) {
107
116
} catch (FirebaseFirestoreException e ) {
108
117
lastWriteError = e ;
109
118
}
119
+ this .writtenDocs .add (key );
110
120
}
111
121
112
122
public void delete (DocumentKey key ) {
113
123
write (Collections .singletonList (new DeleteMutation (key , precondition (key ))));
114
- // Since the delete will be applied before all following writes, we need to ensure that the
115
- // precondition for the next write will be exists: false.
116
- readVersions .put (key , SnapshotVersion .NONE );
124
+ writtenDocs .add (key );
117
125
}
118
126
119
127
public Task <Void > commit () {
@@ -192,7 +200,7 @@ private void recordVersion(MaybeDocument doc) throws FirebaseFirestoreException
192
200
*/
193
201
private Precondition precondition (DocumentKey key ) {
194
202
@ Nullable SnapshotVersion version = readVersions .get (key );
195
- if (version != null ) {
203
+ if (! writtenDocs . contains ( key ) && version != null ) {
196
204
return Precondition .updateTime (version );
197
205
} else {
198
206
return Precondition .NONE ;
@@ -205,20 +213,23 @@ private Precondition precondition(DocumentKey key) {
205
213
*/
206
214
private Precondition preconditionForUpdate (DocumentKey key ) throws FirebaseFirestoreException {
207
215
@ Nullable SnapshotVersion version = this .readVersions .get (key );
208
- if (version != null && version .equals (SnapshotVersion .NONE )) {
209
- // The document to update doesn't exist, so fail the transaction.
210
- //
211
- // This has to be validated locally because you can't send a precondition that a document
212
- // does not exist without changing the semantics of the backend write to be an insert. This is
213
- // the reverse of what we want, since we want to assert that the document doesn't exist but
214
- // then send the update and have it fail. Since we can't express that to the backend, we have
215
- // to validate locally.
216
- //
217
- // Note: this can change once we can send separate verify writes in the transaction.
218
- throw new FirebaseFirestoreException (
219
- "Can't update a document that doesn't exist." , Code .INVALID_ARGUMENT );
220
- } else if (version != null ) {
221
- // Document exists, just base precondition on document update time.
216
+ // The first time a document is written, we want to take into account the read time and
217
+ // existence.
218
+ if (!writtenDocs .contains (key ) && version != null ) {
219
+ if (version != null && version .equals (SnapshotVersion .NONE )) {
220
+ // The document to update doesn't exist, so fail the transaction.
221
+ //
222
+ // This has to be validated locally because you can't send a precondition that a document
223
+ // does not exist without changing the semantics of the backend write to be an insert. This is
224
+ // the reverse of what we want, since we want to assert that the document doesn't exist but
225
+ // then send the update and have it fail. Since we can't express that to the backend, we have
226
+ // to validate locally.
227
+ //
228
+ // Note: this can change once we can send separate verify writes in the transaction.
229
+ throw new FirebaseFirestoreException (
230
+ "Can't update a document that doesn't exist." , Code .INVALID_ARGUMENT );
231
+ }
232
+ // Document exists, base precondition on document update time.
222
233
return Precondition .updateTime (version );
223
234
} else {
224
235
// Document was not read, so we just use the preconditions for a blind write.
0 commit comments