Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2208)

Side by Side Diff: content/browser/indexed_db/indexed_db_backing_store.cc

Issue 1170833004: Indexed DB: Simplify cursor iteration logic (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/indexed_db/indexed_db_backing_store.h" 5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/files/file_path.h" 9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h" 10 #include "base/files/file_util.h"
(...skipping 3156 matching lines...) Expand 10 before | Expand all | Expand 10 after
3167 if (!Continue(s)) 3167 if (!Continue(s))
3168 return false; 3168 return false;
3169 } 3169 }
3170 return true; 3170 return true;
3171 } 3171 }
3172 3172
3173 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key, 3173 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3174 const IndexedDBKey* primary_key, 3174 const IndexedDBKey* primary_key,
3175 IteratorState next_state, 3175 IteratorState next_state,
3176 leveldb::Status* s) { 3176 leveldb::Status* s) {
3177 if (cursor_options_.forward)
3178 return ContinueNext(key, primary_key, next_state, s);
3179 else
3180 return ContinuePrevious(key, primary_key, next_state, s);
3181 }
3182
3183 bool IndexedDBBackingStore::Cursor::ContinueNext(
3184 const IndexedDBKey* key,
3185 const IndexedDBKey* primary_key,
3186 IteratorState next_state,
3187 leveldb::Status* s) {
3188 DCHECK(cursor_options_.forward);
3177 DCHECK(!key || key->IsValid()); 3189 DCHECK(!key || key->IsValid());
3178 DCHECK(!primary_key || primary_key->IsValid()); 3190 DCHECK(!primary_key || primary_key->IsValid());
3179 *s = leveldb::Status::OK(); 3191 *s = leveldb::Status::OK();
3180 3192
3181 // TODO(alecflett): avoid a copy here? 3193 // TODO(alecflett): avoid a copy here?
3182 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey(); 3194 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3183 3195
3184 // When iterating with PrevNoDuplicate, spec requires that the 3196 // Optimization: if seeking to a particular key (or key and primary key),
3185 // value we yield for each key is the first duplicate in forwards 3197 // skip the cursor forward rather than iterating it.
3186 // order. 3198 if (next_state == SEEK && key) {
jsbell 2015/06/09 18:52:19 It should never be the case that key is specified
jsbell 2015/06/10 17:51:57 Done.
3199 std::string leveldb_key;
3200 if (primary_key)
jsbell 2015/06/09 18:52:19 Clearer as ternary (?:) operation?
jsbell 2015/06/10 17:51:57 Done.
3201 leveldb_key = EncodeKey(*key, *primary_key);
3202 else
3203 leveldb_key = EncodeKey(*key);
3204 *s = iterator_->Seek(leveldb_key);
3205 if (!s->ok())
3206 return false;
3207 // Cursor may be at the next value already; don't advance it again below.
cmumford 2015/06/09 17:06:24 Is it a maybe, or is it for sure at the next value
jsbell 2015/06/10 17:51:58 Done. (It may be positioned at a stale entry and
3208 next_state = READY;
3209 }
3210
3211 for (;;) {
3212 // Only advance the cursor if it was not set to position already,
3213 // either because it is newly opened (and positioned at start of range)
3214 // or skipped forward by continue with a specific key.
3215 if (next_state == SEEK) {
3216 *s = iterator_->Next();
3217 if (!s->ok())
3218 return false;
3219 } else {
3220 next_state = SEEK;
3221 }
3222
3223 // TODO(jsbell): Can we DCHECK iterator_->IsValid() up top?
3224 // Fail if we've run out of data or gone past the cursor's bounds.
3225 if (!iterator_->IsValid() || IsPastBounds())
3226 return false;
3227
3228 // TODO(jsbell): Document why this might be false. When do we ever not
3229 // seek into the range before starting cursor iteration?
3230 if (!HaveEnteredRange())
3231 continue;
3232
3233 // The row may not load because there's a stale entry in the
3234 // index. If no error then not fatal.
3235 if (!LoadCurrentRow(s)) {
3236 if (!s->ok())
3237 return false;
3238 continue;
3239 }
3240
3241 // If seeking to a key (or key and primary key), continue until found.
jsbell 2015/06/09 18:52:19 Due to the Seek() optimization above, this block s
jsbell 2015/06/10 17:51:57 Added comment. Will run tests w/ DCHECK and remove
3242 if (key) {
3243 if (primary_key && current_key_->Equals(*key) &&
3244 this->primary_key().IsLessThan(*primary_key))
3245 continue;
3246 if (current_key_->IsLessThan(*key))
3247 continue;
3248 }
3249
jsbell 2015/06/09 18:52:19 Add a comment here: // Cursor is now positioned a
jsbell 2015/06/10 17:51:57 Done.
3250 // "Unique" cursors should continue seeking until a new key value is seen.
3251 if (cursor_options_.unique && previous_key.IsValid() &&
3252 current_key_->Equals(previous_key)) {
3253 continue;
3254 }
3255
3256 break;
3257 }
3258
3259 return true;
3260 }
3261
3262 bool IndexedDBBackingStore::Cursor::ContinuePrevious(
3263 const IndexedDBKey* key,
3264 const IndexedDBKey* primary_key,
3265 IteratorState next_state,
3266 leveldb::Status* s) {
3267 DCHECK(!cursor_options_.forward);
3268 DCHECK(!key || key->IsValid());
3269 DCHECK(!primary_key || primary_key->IsValid());
3270 *s = leveldb::Status::OK();
3271
3272 // TODO(alecflett): avoid a copy here?
3273 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3274
3275 // When iterating with PrevNoDuplicate, spec requires that the value we
3276 // yield for each key is the *first* duplicate in forwards order. We do this
3277 // by remembering the *last* duplicate seen (implicitly, the first value
3278 // seen with a new key), continuing until another new key is seen, then
3279 // reversing until we find a key equal to the *last* duplicate.
3187 IndexedDBKey last_duplicate_key; 3280 IndexedDBKey last_duplicate_key;
3281 bool find_first_duplicate = false;
jsbell 2015/06/09 15:47:32 Renamed from "flipped"
3188 3282
3189 bool forward = cursor_options_.forward; 3283 // TODO(jsbell): Optimize continuing to a specific key (or key and primary
3190 bool first_iteration_forward = forward; 3284 // key) for reverse cursors as well. See Seek() optimization at the start
3191 bool flipped = false; 3285 // of ContinueNext() for an example.
3192 3286
3193 for (;;) { 3287 for (;;) {
3194 if (next_state == SEEK) { 3288 if (next_state == SEEK) {
3195 // TODO(jsbell): Optimize seeking for reverse cursors as well. 3289 *s = iterator_->Prev();
3196 if (first_iteration_forward && key) {
3197 first_iteration_forward = false;
3198 std::string leveldb_key;
3199 if (primary_key) {
3200 leveldb_key = EncodeKey(*key, *primary_key);
3201 } else {
3202 leveldb_key = EncodeKey(*key);
3203 }
3204 *s = iterator_->Seek(leveldb_key);
3205 } else if (forward) {
3206 *s = iterator_->Next();
3207 } else {
3208 *s = iterator_->Prev();
3209 }
3210 if (!s->ok()) 3290 if (!s->ok())
3211 return false; 3291 return false;
3212 } else { 3292 } else {
3213 next_state = SEEK; // for subsequent iterations 3293 next_state = SEEK; // for subsequent iterations
3214 } 3294 }
3215 3295
3216 if (!iterator_->IsValid()) { 3296 // If we've run out of data or gone past the cursor's bounds.
3217 if (!forward && last_duplicate_key.IsValid()) { 3297 if (!iterator_->IsValid() || IsPastBounds()) {
3218 // We need to walk forward because we hit the end of 3298 if (last_duplicate_key.IsValid()) {
3219 // the data. 3299 // Walk the cursor forward to the first duplicate.
3220 forward = true; 3300 find_first_duplicate = true;
3221 flipped = true; 3301 break;
3222 continue;
3223 } 3302 }
3224 3303
3225 return false; 3304 return false;
3226 } 3305 }
3227 3306
3228 if (IsPastBounds()) { 3307 // TODO(jsbell): Document why this might be false. When do we ever not
3229 if (!forward && last_duplicate_key.IsValid()) { 3308 // seek into the range before starting cursor iteration?
3230 // We need to walk forward because now we're beyond the
3231 // bounds defined by the cursor.
3232 forward = true;
3233 flipped = true;
3234 continue;
3235 }
3236
3237 return false;
3238 }
3239
3240 if (!HaveEnteredRange()) 3309 if (!HaveEnteredRange())
3241 continue; 3310 continue;
3242 3311
3243 // The row may not load because there's a stale entry in the 3312 // The row may not load because there's a stale entry in the
3244 // index. If no error then not fatal. 3313 // index. If no error then not fatal.
3245 if (!LoadCurrentRow(s)) { 3314 if (!LoadCurrentRow(s)) {
3246 if (!s->ok()) 3315 if (!s->ok())
3247 return false; 3316 return false;
3248 continue; 3317 continue;
3249 } 3318 }
3250 3319
3320 // If seeking to a key (or key and primary key), continue until found.
3251 if (key) { 3321 if (key) {
3252 if (forward) { 3322 if (primary_key && key->Equals(*current_key_) &&
3253 if (primary_key && current_key_->Equals(*key) && 3323 primary_key->IsLessThan(this->primary_key()))
3254 this->primary_key().IsLessThan(*primary_key)) 3324 continue;
3255 continue; 3325 if (key->IsLessThan(*current_key_))
3256 if (!flipped && current_key_->IsLessThan(*key)) 3326 continue;
3257 continue;
3258 } else {
3259 if (primary_key && key->Equals(*current_key_) &&
3260 primary_key->IsLessThan(this->primary_key()))
3261 continue;
3262 if (key->IsLessThan(*current_key_))
3263 continue;
3264 }
3265 } 3327 }
3266 3328
jsbell 2015/06/09 18:52:19 Add comment here: // Cursor is now positioned at
jsbell 2015/06/10 17:51:57 Done.
3267 if (cursor_options_.unique) { 3329 if (cursor_options_.unique) {
3268 if (previous_key.IsValid() && current_key_->Equals(previous_key)) { 3330 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3269 // We should never be able to walk forward all the way 3331 // We should never be able to walk forward all the way
jsbell 2015/06/09 18:52:19 This comment/DCHECK no longer make sense since dir
jsbell 2015/06/10 17:51:57 Done.
3270 // to the previous key. 3332 // to the previous key.
3271 DCHECK(!last_duplicate_key.IsValid()); 3333 DCHECK(!last_duplicate_key.IsValid());
3272 continue; 3334 continue;
3273 } 3335 }
3274 3336
3275 if (!forward) { 3337 // If we've found a new key, remember it and keep going.
3276 if (!last_duplicate_key.IsValid()) { 3338 if (!last_duplicate_key.IsValid()) {
3277 last_duplicate_key = *current_key_; 3339 last_duplicate_key = *current_key_;
3278 continue;
3279 }
3280
3281 // We need to walk forward because we hit the boundary
3282 // between key ranges.
3283 if (!last_duplicate_key.Equals(*current_key_)) {
3284 forward = true;
3285 flipped = true;
3286 continue;
3287 }
3288
3289 continue; 3340 continue;
3290 } 3341 }
3342
3343 // If we're still seeing duplicates, keep going.
3344 if (last_duplicate_key.Equals(*current_key_))
3345 continue;
3346
3347 // Walk the cursor forward to the first duplicate.
3348 find_first_duplicate = true;
3291 } 3349 }
3350
3292 break; 3351 break;
3293 } 3352 }
3294 3353
3354 // TODO(jsbell): Rather than iterating, stash the last leveldb key of the
3355 // last plausible result, then Seek() the cursor directly to that and
3356 // LoadCurrentRow().
3357
3358 if (find_first_duplicate) {
3359 DCHECK_EQ(next_state, SEEK);
3360 DCHECK(cursor_options_.unique);
3361 for (;;) {
3362 *s = iterator_->Next();
3363 if (!s->ok())
cmumford 2015/06/09 17:06:24 What about: if (!s->ok() || !iterator_->IsValid()
jsbell 2015/06/10 17:51:57 Done.
3364 return false;
3365
3366 if (!iterator_->IsValid() || IsPastBounds())
3367 return false;
3368
3369 // TODO(jsbell): Document why this might be false. When do we ever not
3370 // seek into the range before starting cursor iteration?
3371 if (!HaveEnteredRange())
3372 continue;
3373
3374 // The row may not load because there's a stale entry in the
3375 // index. If no error then not fatal.
3376 if (!LoadCurrentRow(s)) {
3377 if (!s->ok())
3378 return false;
3379 continue;
3380 }
3381
jsbell 2015/06/09 15:47:32 I removed two clauses here from when we were pairi
3382 break;
3383 }
3384 }
3385
3295 DCHECK(!last_duplicate_key.IsValid() || 3386 DCHECK(!last_duplicate_key.IsValid() ||
3296 (forward && last_duplicate_key.Equals(*current_key_))); 3387 (find_first_duplicate && last_duplicate_key.Equals(*current_key_)));
3297 return true; 3388 return true;
3298 } 3389 }
3299 3390
3300 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const { 3391 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3301 if (cursor_options_.forward) { 3392 if (cursor_options_.forward) {
3302 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key); 3393 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3303 if (cursor_options_.low_open) { 3394 if (cursor_options_.low_open) {
3304 return compare > 0; 3395 return compare > 0;
3305 } 3396 }
3306 return compare >= 0; 3397 return compare >= 0;
(...skipping 1125 matching lines...) Expand 10 before | Expand all | Expand 10 after
4432 4523
4433 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor( 4524 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4434 const WriteDescriptor& other) = default; 4525 const WriteDescriptor& other) = default;
4435 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() = 4526 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4436 default; 4527 default;
4437 IndexedDBBackingStore::Transaction::WriteDescriptor& 4528 IndexedDBBackingStore::Transaction::WriteDescriptor&
4438 IndexedDBBackingStore::Transaction::WriteDescriptor:: 4529 IndexedDBBackingStore::Transaction::WriteDescriptor::
4439 operator=(const WriteDescriptor& other) = default; 4530 operator=(const WriteDescriptor& other) = default;
4440 4531
4441 } // namespace content 4532 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698