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

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: Review feedback and comments 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
« no previous file with comments | « content/browser/indexed_db/indexed_db_backing_store.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 DCHECK(!key || next_state == SEEK);
3178
3179 if (cursor_options_.forward)
3180 return ContinueNext(key, primary_key, next_state, s);
3181 else
3182 return ContinuePrevious(key, primary_key, next_state, s);
3183 }
3184
3185 bool IndexedDBBackingStore::Cursor::ContinueNext(
3186 const IndexedDBKey* key,
3187 const IndexedDBKey* primary_key,
3188 IteratorState next_state,
3189 leveldb::Status* s) {
3190 DCHECK(cursor_options_.forward);
3177 DCHECK(!key || key->IsValid()); 3191 DCHECK(!key || key->IsValid());
3178 DCHECK(!primary_key || primary_key->IsValid()); 3192 DCHECK(!primary_key || primary_key->IsValid());
3179 *s = leveldb::Status::OK(); 3193 *s = leveldb::Status::OK();
3180 3194
3181 // TODO(alecflett): avoid a copy here? 3195 // TODO(alecflett): avoid a copy here?
3182 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey(); 3196 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3183 3197
3184 // When iterating with PrevNoDuplicate, spec requires that the 3198 // 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 3199 // skip the cursor forward rather than iterating it.
3186 // order. 3200 if (next_state == SEEK && key) {
3201 std::string leveldb_key =
3202 primary_key ? EncodeKey(*key, *primary_key) : EncodeKey(*key);
3203 *s = iterator_->Seek(leveldb_key);
3204 if (!s->ok())
3205 return false;
3206 // Cursor is at the next value already; don't advance it again below.
3207 next_state = READY;
3208 }
3209
3210 for (;;) {
3211 // Only advance the cursor if it was not set to position already,
3212 // either because it is newly opened (and positioned at start of range)
3213 // or skipped forward by continue with a specific key.
3214 if (next_state == SEEK) {
3215 *s = iterator_->Next();
3216 if (!s->ok())
3217 return false;
3218 } else {
3219 next_state = SEEK;
3220 }
3221
3222 // TODO(jsbell): Can we DCHECK iterator_->IsValid() up top?
3223 // Fail if we've run out of data or gone past the cursor's bounds.
3224 if (!iterator_->IsValid() || IsPastBounds())
3225 return false;
3226
3227 // TODO(jsbell): Document why this might be false. When do we ever not
3228 // seek into the range before starting cursor iteration?
3229 if (!HaveEnteredRange())
3230 continue;
3231
3232 // The row may not load because there's a stale entry in the
3233 // index. If no error then not fatal.
3234 if (!LoadCurrentRow(s)) {
3235 if (!s->ok())
3236 return false;
3237 continue;
3238 }
3239
3240 // If seeking to a key (or key and primary key), continue until found.
3241 // TODO(jsbell): Given the Seek() optimization above, this should
3242 // not be necessary. Verify and remove.
3243 if (key) {
3244 if (primary_key && current_key_->Equals(*key) &&
3245 this->primary_key().IsLessThan(*primary_key))
3246 continue;
3247 if (current_key_->IsLessThan(*key))
3248 continue;
3249 }
3250
3251 // Cursor is now positioned at a non-stale record in range.
3252
3253 // "Unique" cursors should continue seeking until a new key value is seen.
3254 if (cursor_options_.unique && previous_key.IsValid() &&
3255 current_key_->Equals(previous_key)) {
3256 continue;
3257 }
3258
3259 break;
3260 }
3261
3262 return true;
3263 }
3264
3265 bool IndexedDBBackingStore::Cursor::ContinuePrevious(
3266 const IndexedDBKey* key,
3267 const IndexedDBKey* primary_key,
3268 IteratorState next_state,
3269 leveldb::Status* s) {
3270 DCHECK(!cursor_options_.forward);
3271 DCHECK(!key || key->IsValid());
3272 DCHECK(!primary_key || primary_key->IsValid());
3273 *s = leveldb::Status::OK();
3274
3275 // TODO(alecflett): avoid a copy here?
3276 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3277
3278 // When iterating with PrevNoDuplicate, spec requires that the value we
3279 // yield for each key is the *first* duplicate in forwards order. We do this
3280 // by remembering the *last* duplicate seen (implicitly, the first value
3281 // seen with a new key), continuing until another new key is seen, then
3282 // reversing until we find a key equal to the *last* duplicate.
3187 IndexedDBKey last_duplicate_key; 3283 IndexedDBKey last_duplicate_key;
3284 bool find_first_duplicate = false;
3188 3285
3189 bool forward = cursor_options_.forward; 3286 // TODO(jsbell): Optimize continuing to a specific key (or key and primary
3190 bool first_iteration_forward = forward; 3287 // key) for reverse cursors as well. See Seek() optimization at the start
3191 bool flipped = false; 3288 // of ContinueNext() for an example.
3192 3289
3193 for (;;) { 3290 for (;;) {
3194 if (next_state == SEEK) { 3291 if (next_state == SEEK) {
3195 // TODO(jsbell): Optimize seeking for reverse cursors as well. 3292 *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()) 3293 if (!s->ok())
3211 return false; 3294 return false;
3212 } else { 3295 } else {
3213 next_state = SEEK; // for subsequent iterations 3296 next_state = SEEK; // for subsequent iterations
3214 } 3297 }
3215 3298
3216 if (!iterator_->IsValid()) { 3299 // If we've run out of data or gone past the cursor's bounds.
3217 if (!forward && last_duplicate_key.IsValid()) { 3300 if (!iterator_->IsValid() || IsPastBounds()) {
3218 // We need to walk forward because we hit the end of 3301 if (last_duplicate_key.IsValid()) {
3219 // the data. 3302 // Walk the cursor forward to the first duplicate.
3220 forward = true; 3303 find_first_duplicate = true;
3221 flipped = true; 3304 break;
3222 continue;
3223 } 3305 }
3224 3306
3225 return false; 3307 return false;
3226 } 3308 }
3227 3309
3228 if (IsPastBounds()) { 3310 // TODO(jsbell): Document why this might be false. When do we ever not
3229 if (!forward && last_duplicate_key.IsValid()) { 3311 // 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()) 3312 if (!HaveEnteredRange())
3241 continue; 3313 continue;
3242 3314
3243 // The row may not load because there's a stale entry in the 3315 // The row may not load because there's a stale entry in the
3244 // index. If no error then not fatal. 3316 // index. If no error then not fatal.
3245 if (!LoadCurrentRow(s)) { 3317 if (!LoadCurrentRow(s)) {
3246 if (!s->ok()) 3318 if (!s->ok())
3247 return false; 3319 return false;
3248 continue; 3320 continue;
3249 } 3321 }
3250 3322
3323 // If seeking to a key (or key and primary key), continue until found.
3324 // TODO(jsbell): If Seek() optimization is added above, remove this.
3251 if (key) { 3325 if (key) {
3252 if (forward) { 3326 if (primary_key && key->Equals(*current_key_) &&
3253 if (primary_key && current_key_->Equals(*key) && 3327 primary_key->IsLessThan(this->primary_key()))
3254 this->primary_key().IsLessThan(*primary_key)) 3328 continue;
3255 continue; 3329 if (key->IsLessThan(*current_key_))
3256 if (!flipped && current_key_->IsLessThan(*key)) 3330 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 } 3331 }
3266 3332
3333 // Cursor is now positioned at a non-stale record in range.
3334
3267 if (cursor_options_.unique) { 3335 if (cursor_options_.unique) {
3268 if (previous_key.IsValid() && current_key_->Equals(previous_key)) { 3336 // If entry is a duplicate, keep going. Although the cursor should be
3269 // We should never be able to walk forward all the way 3337 // positioned at the first duplicate already, new duplicates may have
3270 // to the previous key. 3338 // been inserted since the cursor was last iterated, and should be
3271 DCHECK(!last_duplicate_key.IsValid()); 3339 // skipped to maintain "unique" iteration.
3340 if (previous_key.IsValid() && current_key_->Equals(previous_key))
3341 continue;
3342
3343 // If we've found a new key, remember it and keep going.
3344 if (!last_duplicate_key.IsValid()) {
3345 last_duplicate_key = *current_key_;
3272 continue; 3346 continue;
3273 } 3347 }
3274 3348
3275 if (!forward) { 3349 // If we're still seeing duplicates, keep going.
3276 if (!last_duplicate_key.IsValid()) { 3350 if (last_duplicate_key.Equals(*current_key_))
3277 last_duplicate_key = *current_key_; 3351 continue;
3278 continue;
3279 }
3280 3352
3281 // We need to walk forward because we hit the boundary 3353 // Walk the cursor forward to the first duplicate.
3282 // between key ranges. 3354 find_first_duplicate = true;
3283 if (!last_duplicate_key.Equals(*current_key_)) { 3355 }
3284 forward = true;
3285 flipped = true;
3286 continue;
3287 }
3288 3356
3289 continue;
3290 }
3291 }
3292 break; 3357 break;
3293 } 3358 }
3294 3359
3360 // TODO(jsbell): Rather than iterating, stash the last leveldb key of the
3361 // last plausible result, then Seek() the cursor directly to that and
3362 // LoadCurrentRow().
3363
3364 if (find_first_duplicate) {
3365 DCHECK_EQ(next_state, SEEK);
3366 DCHECK(cursor_options_.unique);
3367 for (;;) {
3368 *s = iterator_->Next();
3369 if (!s->ok() || !iterator_->IsValid() || IsPastBounds())
3370 return false;
3371
3372 // TODO(jsbell): Document why this might be false. When do we ever not
3373 // seek into the range before starting cursor iteration?
3374 if (!HaveEnteredRange())
3375 continue;
3376
3377 // The row may not load because there's a stale entry in the
3378 // index. If no error then not fatal.
3379 if (!LoadCurrentRow(s)) {
3380 if (!s->ok())
3381 return false;
3382 continue;
3383 }
3384
3385 break;
3386 }
3387 }
3388
3295 DCHECK(!last_duplicate_key.IsValid() || 3389 DCHECK(!last_duplicate_key.IsValid() ||
3296 (forward && last_duplicate_key.Equals(*current_key_))); 3390 (find_first_duplicate && last_duplicate_key.Equals(*current_key_)));
3297 return true; 3391 return true;
3298 } 3392 }
3299 3393
3300 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const { 3394 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3301 if (cursor_options_.forward) { 3395 if (cursor_options_.forward) {
3302 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key); 3396 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3303 if (cursor_options_.low_open) { 3397 if (cursor_options_.low_open) {
3304 return compare > 0; 3398 return compare > 0;
3305 } 3399 }
3306 return compare >= 0; 3400 return compare >= 0;
(...skipping 1125 matching lines...) Expand 10 before | Expand all | Expand 10 after
4432 4526
4433 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor( 4527 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4434 const WriteDescriptor& other) = default; 4528 const WriteDescriptor& other) = default;
4435 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() = 4529 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4436 default; 4530 default;
4437 IndexedDBBackingStore::Transaction::WriteDescriptor& 4531 IndexedDBBackingStore::Transaction::WriteDescriptor&
4438 IndexedDBBackingStore::Transaction::WriteDescriptor:: 4532 IndexedDBBackingStore::Transaction::WriteDescriptor::
4439 operator=(const WriteDescriptor& other) = default; 4533 operator=(const WriteDescriptor& other) = default;
4440 4534
4441 } // namespace content 4535 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/indexed_db/indexed_db_backing_store.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698