41
41
42
42
final class SQLiteRemoteDocumentCache implements RemoteDocumentCache {
43
43
/** The number of bind args per collection group in {@link #getAll(String, IndexOffset, int)} */
44
- @ VisibleForTesting static final int GET_ALL_BINDS_PER_STATEMENT = 8 ;
44
+ @ VisibleForTesting static final int BINDS_PER_STATEMENT = 9 ;
45
45
46
46
private final SQLitePersistence db ;
47
47
private final LocalSerializer serializer ;
@@ -83,14 +83,12 @@ public void add(MutableDocument document, SnapshotVersion readTime) {
83
83
@ Override
84
84
public void remove (DocumentKey documentKey ) {
85
85
String path = EncodedPath .encode (documentKey .getPath ());
86
-
87
86
db .execute ("DELETE FROM remote_documents WHERE path = ?" , path );
88
87
}
89
88
90
89
@ Override
91
90
public MutableDocument get (DocumentKey documentKey ) {
92
91
String path = EncodedPath .encode (documentKey .getPath ());
93
-
94
92
MutableDocument document =
95
93
db .query (
96
94
"SELECT contents, read_time_seconds, read_time_nanos "
@@ -145,12 +143,12 @@ public Map<DocumentKey, MutableDocument> getAll(
145
143
146
144
if (collections .isEmpty ()) {
147
145
return Collections .emptyMap ();
148
- } else if (GET_ALL_BINDS_PER_STATEMENT * collections .size () < SQLitePersistence .MAX_ARGS ) {
146
+ } else if (BINDS_PER_STATEMENT * collections .size () < SQLitePersistence .MAX_ARGS ) {
149
147
return getAll (collections , offset , count );
150
148
} else {
151
149
// We need to fan out our collection scan since SQLite only supports 999 binds per statement.
152
150
Map <DocumentKey , MutableDocument > results = new HashMap <>();
153
- int pageSize = SQLitePersistence .MAX_ARGS / GET_ALL_BINDS_PER_STATEMENT ;
151
+ int pageSize = SQLitePersistence .MAX_ARGS / BINDS_PER_STATEMENT ;
154
152
for (int i = 0 ; i < collections .size (); i += pageSize ) {
155
153
results .putAll (
156
154
getAll (
@@ -168,26 +166,25 @@ private Map<DocumentKey, MutableDocument> getAll(
168
166
Timestamp readTime = offset .getReadTime ().getTimestamp ();
169
167
DocumentKey documentKey = offset .getDocumentKey ();
170
168
171
- // TODO(indexing): Add path_length
172
-
173
169
StringBuilder sql =
174
170
repeatSequence (
175
171
"SELECT contents, read_time_seconds, read_time_nanos, path "
176
172
+ "FROM remote_documents "
177
- + "WHERE path >= ? AND path < ? "
173
+ + "WHERE path >= ? AND path < ? AND path_length = ? "
178
174
+ "AND (read_time_seconds > ? OR ( "
179
175
+ "read_time_seconds = ? AND read_time_nanos > ?) OR ( "
180
176
+ "read_time_seconds = ? AND read_time_nanos = ? and path > ?)) " ,
181
177
collections .size (),
182
178
" UNION " );
183
179
sql .append ("ORDER BY read_time_seconds, read_time_nanos, path LIMIT ?" );
184
180
185
- Object [] bindVars = new Object [GET_ALL_BINDS_PER_STATEMENT * collections .size () + 1 ];
181
+ Object [] bindVars = new Object [BINDS_PER_STATEMENT * collections .size () + 1 ];
186
182
int i = 0 ;
187
- for (ResourcePath collectionParent : collections ) {
188
- String prefixPath = EncodedPath .encode (collectionParent );
183
+ for (ResourcePath collection : collections ) {
184
+ String prefixPath = EncodedPath .encode (collection );
189
185
bindVars [i ++] = prefixPath ;
190
186
bindVars [i ++] = EncodedPath .prefixSuccessor (prefixPath );
187
+ bindVars [i ++] = collection .length () + 1 ;
191
188
bindVars [i ++] = readTime .getSeconds ();
192
189
bindVars [i ++] = readTime .getSeconds ();
193
190
bindVars [i ++] = readTime .getNanoseconds ();
@@ -236,73 +233,17 @@ public ImmutableSortedMap<DocumentKey, MutableDocument> getAllDocumentsMatchingQ
236
233
!query .isCollectionGroupQuery (),
237
234
"CollectionGroup queries should be handled in LocalDocumentsView" );
238
235
239
- StringBuilder sql =
240
- new StringBuilder (
241
- "SELECT contents, read_time_seconds, read_time_nanos "
242
- + "FROM remote_documents WHERE path >= ? AND path < ? AND path_length = ?" );
243
-
244
- Object [] bindVars = new Object [3 + (IndexOffset .NONE .equals (offset ) ? 0 : 6 )];
245
-
246
- String prefix = EncodedPath .encode (query .getPath ());
247
-
248
- int i = 0 ;
249
- bindVars [i ++] = prefix ;
250
- bindVars [i ++] = EncodedPath .prefixSuccessor (prefix );
251
- bindVars [i ++] = query .getPath ().length () + 1 ;
252
-
253
- if (!IndexOffset .NONE .equals (offset )) {
254
- Timestamp readTime = offset .getReadTime ().getTimestamp ();
255
- DocumentKey documentKey = offset .getDocumentKey ();
256
-
257
- sql .append (
258
- " AND (read_time_seconds > ? OR ("
259
- + "read_time_seconds = ? AND read_time_nanos > ?) OR ("
260
- + "read_time_seconds = ? AND read_time_nanos = ? and path > ?))" );
261
- bindVars [i ++] = readTime .getSeconds ();
262
- bindVars [i ++] = readTime .getSeconds ();
263
- bindVars [i ++] = readTime .getNanoseconds ();
264
- bindVars [i ++] = readTime .getSeconds ();
265
- bindVars [i ++] = readTime .getNanoseconds ();
266
- bindVars [i ] = EncodedPath .encode (documentKey .getPath ());
267
- }
268
-
269
- ImmutableSortedMap <DocumentKey , MutableDocument >[] results =
270
- (ImmutableSortedMap <DocumentKey , MutableDocument >[])
271
- new ImmutableSortedMap [] {DocumentCollections .emptyMutableDocumentMap ()};
272
- BackgroundQueue backgroundQueue = new BackgroundQueue ();
273
-
274
- db .query (sql .toString ())
275
- .binding (bindVars )
276
- .forEach (
277
- row -> {
278
- // Store row values in array entries to provide the correct context inside the
279
- // executor.
280
- final byte [] rawDocument = row .getBlob (0 );
281
- final int [] readTimeSeconds = {row .getInt (1 )};
282
- final int [] readTimeNanos = {row .getInt (2 )};
283
-
284
- // Since scheduling background tasks incurs overhead, we only dispatch to a
285
- // background thread if there are still some documents remaining.
286
- Executor executor = row .isLast () ? Executors .DIRECT_EXECUTOR : backgroundQueue ;
287
- executor .execute (
288
- () -> {
289
- MutableDocument document =
290
- decodeMaybeDocument (rawDocument , readTimeSeconds [0 ], readTimeNanos [0 ]);
291
- if (document .isFoundDocument () && query .matches (document )) {
292
- synchronized (SQLiteRemoteDocumentCache .this ) {
293
- results [0 ] = results [0 ].insert (document .getKey (), document );
294
- }
295
- }
296
- });
297
- });
298
-
299
- try {
300
- backgroundQueue .drain ();
301
- } catch (InterruptedException e ) {
302
- fail ("Interrupted while deserializing documents" , e );
236
+ Map <DocumentKey , MutableDocument > allDocuments =
237
+ getAll (Collections .singletonList (query .getPath ()), offset , Integer .MAX_VALUE );
238
+ ImmutableSortedMap <DocumentKey , MutableDocument > matchingDocuments =
239
+ DocumentCollections .emptyMutableDocumentMap ();
240
+ for (MutableDocument document : allDocuments .values ()) {
241
+ if (document .isFoundDocument () && query .matches (document )) {
242
+ matchingDocuments = matchingDocuments .insert (document .getKey (), document );
243
+ }
303
244
}
304
245
305
- return results [ 0 ] ;
246
+ return matchingDocuments ;
306
247
}
307
248
308
249
@ Override
0 commit comments