Skip to content

Commit 58c2075

Browse files
author
Greg Soltis
authored
Add migration to add sequence numbers to documents (#64)
1 parent 0bd8a18 commit 58c2075

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteSchema.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ class SQLiteSchema {
3838
* The version of the schema. Increase this by one for each migration added to runMigrations
3939
* below.
4040
*/
41-
static final int VERSION = (Persistence.INDEXING_SUPPORT_ENABLED) ? 7 : 6;
41+
static final int VERSION = 7;
42+
// Remove this constant and increment VERSION to enable indexing support
43+
static final int INDEXING_SUPPORT_VERSION = VERSION + 1;
4244

4345
private final SQLiteDatabase db;
4446

@@ -99,6 +101,10 @@ void runMigrations(int fromVersion, int toVersion) {
99101
}
100102

101103
if (fromVersion < 7 && toVersion >= 7) {
104+
ensureSequenceNumbers();
105+
}
106+
107+
if (fromVersion < INDEXING_SUPPORT_VERSION && toVersion >= INDEXING_SUPPORT_VERSION) {
102108
Preconditions.checkState(Persistence.INDEXING_SUPPORT_ENABLED);
103109
createLocalDocumentsCollectionIndex();
104110
}
@@ -245,4 +251,34 @@ private void addTargetCount() {
245251
private void addSequenceNumber() {
246252
db.execSQL("ALTER TABLE target_documents ADD COLUMN sequence_number INTEGER");
247253
}
254+
255+
/**
256+
* Ensures that each entry in the remote document cache has a corresponding sentinel row. Any
257+
* entries that lack a sentinel row are given one with the sequence number set to the highest
258+
* recorded sequence number from the target metadata.
259+
*/
260+
private void ensureSequenceNumbers() {
261+
// Get the current highest sequence number
262+
SQLitePersistence.Query sequenceNumberQuery =
263+
new SQLitePersistence.Query(
264+
db, "SELECT highest_listen_sequence_number FROM target_globals LIMIT 1");
265+
Long boxedSequenceNumber = sequenceNumberQuery.firstValue(c -> c.getLong(0));
266+
hardAssert(boxedSequenceNumber != null, "Missing highest sequence number");
267+
268+
long sequenceNumber = boxedSequenceNumber;
269+
SQLiteStatement tagDocument =
270+
db.compileStatement(
271+
"INSERT INTO target_documents (target_id, path, sequence_number) VALUES (0, ?, ?)");
272+
SQLitePersistence.Query untaggedDocumentsQuery =
273+
new SQLitePersistence.Query(
274+
db,
275+
"SELECT RD.path FROM remote_documents AS RD WHERE NOT EXISTS (SELECT TD.path FROM target_documents AS TD WHERE RD.path = TD.path AND TD.target_id = 0)");
276+
untaggedDocumentsQuery.forEach(
277+
row -> {
278+
tagDocument.clearBindings();
279+
tagDocument.bindString(1, row.getString(0));
280+
tagDocument.bindLong(2, sequenceNumber);
281+
hardAssert(tagDocument.executeInsert() != -1, "Failed to insert a sentinel row");
282+
});
283+
}
248284
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteSchemaTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,49 @@ private void addMutationBatch(SQLiteDatabase db, int batchId, String uid, String
189189
new Object[] {uid, batchId, write.build().toByteArray()});
190190
}
191191

192+
@Test
193+
public void addsSentinelRows() {
194+
schema.runMigrations(0, 6);
195+
196+
long oldSequenceNumber = 1;
197+
// Set the highest sequence number to this value so that untagged documents
198+
// will pick up this value.
199+
long newSequenceNumber = 2;
200+
db.execSQL(
201+
"UPDATE target_globals SET highest_listen_sequence_number = ?",
202+
new Object[] {newSequenceNumber});
203+
204+
// Set up some documents (we only need the keys)
205+
// For the odd ones, add sentinel rows.
206+
for (int i = 0; i < 10; i++) {
207+
String path = "docs/doc_" + i;
208+
db.execSQL("INSERT INTO remote_documents (path) VALUES (?)", new String[] {path});
209+
if (i % 2 == 1) {
210+
db.execSQL(
211+
"INSERT INTO target_documents (target_id, path, sequence_number) VALUES (0, ?, ?)",
212+
new Object[] {path, oldSequenceNumber});
213+
}
214+
}
215+
216+
schema.runMigrations(6, 7);
217+
218+
// Verify.
219+
new SQLitePersistence.Query(
220+
db, "SELECT path, sequence_number FROM target_documents WHERE target_id = 0")
221+
.forEach(
222+
row -> {
223+
String path = row.getString(0);
224+
long sequenceNumber = row.getLong(1);
225+
226+
int docNum = Integer.parseInt(path.split("_")[1]);
227+
// The even documents were missing sequence numbers, they should now be filled in
228+
// to have the new sequence number. The odd documents should have their
229+
// sequence number unchanged, and so be the old value.
230+
long expected = docNum % 2 == 1 ? oldSequenceNumber : newSequenceNumber;
231+
assertEquals(expected, sequenceNumber);
232+
});
233+
}
234+
192235
private void assertNoResultsForQuery(String query, String[] args) {
193236
Cursor cursor = null;
194237
try {

0 commit comments

Comments
 (0)