| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "sync/syncable/mutable_entry.h" | 5 #include "sync/syncable/mutable_entry.h" |
| 6 | 6 |
| 7 #include "base/memory/scoped_ptr.h" | 7 #include "base/memory/scoped_ptr.h" |
| 8 #include "sync/internal_api/public/base/node_ordinal.h" | 8 #include "sync/internal_api/public/base/unique_position.h" |
| 9 #include "sync/syncable/directory.h" | 9 #include "sync/syncable/directory.h" |
| 10 #include "sync/syncable/scoped_index_updater.h" | 10 #include "sync/syncable/scoped_index_updater.h" |
| 11 #include "sync/syncable/scoped_kernel_lock.h" | 11 #include "sync/syncable/scoped_kernel_lock.h" |
| 12 #include "sync/syncable/scoped_parent_child_index_updater.h" |
| 12 #include "sync/syncable/syncable-inl.h" | 13 #include "sync/syncable/syncable-inl.h" |
| 13 #include "sync/syncable/syncable_changes_version.h" | 14 #include "sync/syncable/syncable_changes_version.h" |
| 14 #include "sync/syncable/syncable_util.h" | 15 #include "sync/syncable/syncable_util.h" |
| 15 #include "sync/syncable/write_transaction.h" | 16 #include "sync/syncable/write_transaction.h" |
| 16 | 17 |
| 17 using std::string; | 18 using std::string; |
| 18 | 19 |
| 19 namespace syncer { | 20 namespace syncer { |
| 20 namespace syncable { | 21 namespace syncable { |
| 21 | 22 |
| 22 MutableEntry::MutableEntry(WriteTransaction* trans, Create, | 23 void MutableEntry::Init(WriteTransaction* trans, |
| 23 const Id& parent_id, const string& name) | 24 ModelType model_type, |
| 24 : Entry(trans), | 25 const Id& parent_id, |
| 25 write_transaction_(trans) { | |
| 26 Init(trans, parent_id, name); | |
| 27 } | |
| 28 | |
| 29 | |
| 30 void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id, | |
| 31 const string& name) { | 26 const string& name) { |
| 32 scoped_ptr<EntryKernel> kernel(new EntryKernel); | 27 scoped_ptr<EntryKernel> kernel(new EntryKernel); |
| 33 kernel_ = NULL; | 28 kernel_ = NULL; |
| 34 | 29 |
| 35 kernel->put(ID, trans->directory_->NextId()); | 30 kernel->put(ID, trans->directory_->NextId()); |
| 36 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); | 31 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); |
| 37 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); | 32 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); |
| 38 kernel->put(PARENT_ID, parent_id); | 33 kernel->put(PARENT_ID, parent_id); |
| 39 kernel->put(NON_UNIQUE_NAME, name); | 34 kernel->put(NON_UNIQUE_NAME, name); |
| 40 const base::Time& now = base::Time::Now(); | 35 const base::Time& now = base::Time::Now(); |
| 41 kernel->put(CTIME, now); | 36 kernel->put(CTIME, now); |
| 42 kernel->put(MTIME, now); | 37 kernel->put(MTIME, now); |
| 43 // We match the database defaults here | 38 // We match the database defaults here |
| 44 kernel->put(BASE_VERSION, CHANGES_VERSION); | 39 kernel->put(BASE_VERSION, CHANGES_VERSION); |
| 45 kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal()); | 40 |
| 46 if (!trans->directory()->InsertEntry(trans, kernel.get())) { | 41 // Normally the SPECIFICS setting code is wrapped in logic to deal with |
| 47 return; // We failed inserting, nothing more to do. | 42 // unknown fields and encryption. Since all we want to do here is ensure that |
| 48 } | 43 // GetModelType() returns a correct value from the very beginning, these |
| 44 // few lines are sufficient. |
| 45 sync_pb::EntitySpecifics specifics; |
| 46 AddDefaultFieldValue(model_type, &specifics); |
| 47 kernel->put(SPECIFICS, specifics); |
| 48 |
| 49 // Because this entry is new, it was originally deleted. | 49 // Because this entry is new, it was originally deleted. |
| 50 kernel->put(IS_DEL, true); | 50 kernel->put(IS_DEL, true); |
| 51 trans->SaveOriginal(kernel.get()); | 51 trans->SaveOriginal(kernel.get()); |
| 52 kernel->put(IS_DEL, false); | 52 kernel->put(IS_DEL, false); |
| 53 | 53 |
| 54 // Now swap the pointers. | 54 // Now swap the pointers. |
| 55 kernel_ = kernel.release(); | 55 kernel_ = kernel.release(); |
| 56 } | 56 } |
| 57 | 57 |
| 58 MutableEntry::MutableEntry(WriteTransaction* trans, CreateBookmark, |
| 59 const Id& parent_id, const string& name) |
| 60 : Entry(trans), |
| 61 write_transaction_(trans) { |
| 62 Init(trans, BOOKMARKS, parent_id, name); |
| 63 |
| 64 std::string unique_tag = syncable::GenerateSyncableBookmarkHash( |
| 65 trans->directory()->cache_guid(), Get(ID).value()); |
| 66 |
| 67 kernel_->put(UNIQUE_BOOKMARK_TAG, unique_tag); |
| 68 kernel_->put(UNIQUE_POSITION, |
| 69 UniquePosition::InitialPosition(unique_tag)); |
| 70 trans->directory()->InsertEntry(trans, kernel_); |
| 71 } |
| 72 |
| 73 MutableEntry::MutableEntry(WriteTransaction* trans, |
| 74 CreateUnique, |
| 75 ModelType model_type, |
| 76 const Id& parent_id, |
| 77 const string& name) |
| 78 : Entry(trans), |
| 79 write_transaction_(trans) { |
| 80 DCHECK_NE(model_type, BOOKMARKS); |
| 81 Init(trans, model_type, parent_id, name); |
| 82 trans->directory()->InsertEntry(trans, kernel_); |
| 83 } |
| 84 |
| 58 MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, | 85 MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, |
| 59 const Id& id) | 86 const Id& id) |
| 60 : Entry(trans), write_transaction_(trans) { | 87 : Entry(trans), write_transaction_(trans) { |
| 61 Entry same_id(trans, GET_BY_ID, id); | 88 Entry same_id(trans, GET_BY_ID, id); |
| 62 kernel_ = NULL; | 89 kernel_ = NULL; |
| 63 if (same_id.good()) { | 90 if (same_id.good()) { |
| 64 return; // already have an item with this ID. | 91 return; // already have an item with this ID. |
| 65 } | 92 } |
| 66 scoped_ptr<EntryKernel> kernel(new EntryKernel()); | 93 scoped_ptr<EntryKernel> kernel(new EntryKernel()); |
| 67 | 94 |
| 68 kernel->put(ID, id); | 95 kernel->put(ID, id); |
| 69 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); | 96 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); |
| 70 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); | 97 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); |
| 71 kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal()); | |
| 72 kernel->put(IS_DEL, true); | 98 kernel->put(IS_DEL, true); |
| 73 // We match the database defaults here | 99 // We match the database defaults here |
| 74 kernel->put(BASE_VERSION, CHANGES_VERSION); | 100 kernel->put(BASE_VERSION, CHANGES_VERSION); |
| 75 if (!trans->directory()->InsertEntry(trans, kernel.get())) { | 101 if (!trans->directory()->InsertEntry(trans, kernel.get())) { |
| 76 return; // Failed inserting. | 102 return; // Failed inserting. |
| 77 } | 103 } |
| 78 trans->SaveOriginal(kernel.get()); | 104 trans->SaveOriginal(kernel.get()); |
| 79 | 105 |
| 80 kernel_ = kernel.release(); | 106 kernel_ = kernel.release(); |
| 81 } | 107 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 99 : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { | 125 : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { |
| 100 } | 126 } |
| 101 | 127 |
| 102 bool MutableEntry::PutIsDel(bool is_del) { | 128 bool MutableEntry::PutIsDel(bool is_del) { |
| 103 DCHECK(kernel_); | 129 DCHECK(kernel_); |
| 104 write_transaction_->SaveOriginal(kernel_); | 130 write_transaction_->SaveOriginal(kernel_); |
| 105 if (is_del == kernel_->ref(IS_DEL)) { | 131 if (is_del == kernel_->ref(IS_DEL)) { |
| 106 return true; | 132 return true; |
| 107 } | 133 } |
| 108 if (is_del) { | 134 if (is_del) { |
| 109 if (!UnlinkFromOrder()) { | |
| 110 return false; | |
| 111 } | |
| 112 | |
| 113 // If the server never knew about this item and it's deleted then we don't | 135 // If the server never knew about this item and it's deleted then we don't |
| 114 // need to keep it around. Unsetting IS_UNSYNCED will: | 136 // need to keep it around. Unsetting IS_UNSYNCED will: |
| 115 // - Ensure that the item is never committed to the server. | 137 // - Ensure that the item is never committed to the server. |
| 116 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other | 138 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other |
| 117 // clients to override this entry. | 139 // clients to override this entry. |
| 118 // - Let us delete this entry permanently through | 140 // - Let us delete this entry permanently through |
| 119 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. | 141 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. |
| 120 // This will save memory and avoid crbug.com/125381. | 142 // This will save memory and avoid crbug.com/125381. |
| 121 if (!Get(ID).ServerKnows()) { | 143 if (!Get(ID).ServerKnows()) { |
| 122 Put(IS_UNSYNCED, false); | 144 Put(IS_UNSYNCED, false); |
| 123 } | 145 } |
| 124 } | 146 } |
| 125 | 147 |
| 126 { | 148 { |
| 127 ScopedKernelLock lock(dir()); | 149 ScopedKernelLock lock(dir()); |
| 128 // Some indices don't include deleted items and must be updated | 150 // Some indices don't include deleted items and must be updated |
| 129 // upon a value change. | 151 // upon a value change. |
| 130 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_, | 152 ScopedParentChildIndexUpdater updater(lock, kernel_, |
| 131 dir()->kernel_->parent_id_child_index); | 153 dir()->kernel_->parent_child_index); |
| 132 | 154 |
| 133 kernel_->put(IS_DEL, is_del); | 155 kernel_->put(IS_DEL, is_del); |
| 134 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); | 156 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); |
| 135 } | 157 } |
| 136 | 158 |
| 137 if (!is_del) | |
| 138 // Restores position to the 0th index. | |
| 139 if (!PutPredecessor(Id())) { | |
| 140 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. | |
| 141 NOTREACHED(); | |
| 142 } | |
| 143 | |
| 144 return true; | 159 return true; |
| 145 } | 160 } |
| 146 | 161 |
| 147 bool MutableEntry::Put(Int64Field field, const int64& value) { | 162 bool MutableEntry::Put(Int64Field field, const int64& value) { |
| 148 DCHECK(kernel_); | 163 DCHECK(kernel_); |
| 149 write_transaction_->SaveOriginal(kernel_); | 164 write_transaction_->SaveOriginal(kernel_); |
| 150 if (kernel_->ref(field) != value) { | 165 if (kernel_->ref(field) != value) { |
| 151 ScopedKernelLock lock(dir()); | 166 ScopedKernelLock lock(dir()); |
| 152 kernel_->put(field, value); | 167 kernel_->put(field, value); |
| 153 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); | 168 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 166 } | 181 } |
| 167 | 182 |
| 168 bool MutableEntry::Put(IdField field, const Id& value) { | 183 bool MutableEntry::Put(IdField field, const Id& value) { |
| 169 DCHECK(kernel_); | 184 DCHECK(kernel_); |
| 170 write_transaction_->SaveOriginal(kernel_); | 185 write_transaction_->SaveOriginal(kernel_); |
| 171 if (kernel_->ref(field) != value) { | 186 if (kernel_->ref(field) != value) { |
| 172 if (ID == field) { | 187 if (ID == field) { |
| 173 if (!dir()->ReindexId(write_transaction(), kernel_, value)) | 188 if (!dir()->ReindexId(write_transaction(), kernel_, value)) |
| 174 return false; | 189 return false; |
| 175 } else if (PARENT_ID == field) { | 190 } else if (PARENT_ID == field) { |
| 176 PutParentIdPropertyOnly(value); // Makes sibling order inconsistent. | 191 PutParentIdPropertyOnly(value); |
| 177 // Fixes up the sibling order inconsistency. | 192 if (!Get(IS_DEL)) { |
| 178 if (!PutPredecessor(Id())) { | 193 if (!PutPredecessor(Id())) { |
| 179 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. | 194 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. |
| 180 NOTREACHED(); | 195 NOTREACHED(); |
| 196 } |
| 181 } | 197 } |
| 182 } else { | 198 } else { |
| 183 kernel_->put(field, value); | 199 kernel_->put(field, value); |
| 184 } | 200 } |
| 185 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); | 201 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); |
| 186 } | 202 } |
| 187 return true; | 203 return true; |
| 188 } | 204 } |
| 189 | 205 |
| 190 bool MutableEntry::Put(OrdinalField field, const NodeOrdinal& value) { | 206 bool MutableEntry::Put(UniquePositionField field, const UniquePosition& value) { |
| 191 DCHECK(kernel_); | 207 DCHECK(kernel_); |
| 192 DCHECK(value.IsValid()); | |
| 193 write_transaction_->SaveOriginal(kernel_); | 208 write_transaction_->SaveOriginal(kernel_); |
| 194 if(!kernel_->ref(field).Equals(value)) { | 209 if(!kernel_->ref(field).Equals(value)) { |
| 210 // We should never overwrite a valid position with an invalid one. |
| 211 DCHECK(value.IsValid()); |
| 195 ScopedKernelLock lock(dir()); | 212 ScopedKernelLock lock(dir()); |
| 196 if (SERVER_ORDINAL_IN_PARENT == field) { | 213 if (UNIQUE_POSITION == field) { |
| 197 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater( | 214 ScopedParentChildIndexUpdater updater( |
| 198 lock, kernel_, dir()->kernel_->parent_id_child_index); | 215 lock, kernel_, dir()->kernel_->parent_child_index); |
| 199 kernel_->put(field, value); | 216 kernel_->put(field, value); |
| 200 } else { | 217 } else { |
| 201 kernel_->put(field, value); | 218 kernel_->put(field, value); |
| 202 } | 219 } |
| 203 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); | 220 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); |
| 204 } | 221 } |
| 205 return true; | 222 return true; |
| 206 } | 223 } |
| 207 | 224 |
| 208 void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { | 225 void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 344 write_transaction())) { | 361 write_transaction())) { |
| 345 return false; | 362 return false; |
| 346 } | 363 } |
| 347 } | 364 } |
| 348 kernel_->put(field, value); | 365 kernel_->put(field, value); |
| 349 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); | 366 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); |
| 350 } | 367 } |
| 351 return true; | 368 return true; |
| 352 } | 369 } |
| 353 | 370 |
| 354 bool MutableEntry::UnlinkFromOrder() { | 371 void MutableEntry::PutUniqueBookmarkTag(const std::string& tag) { |
| 355 ScopedKernelLock lock(dir()); | 372 // This unique tag will eventually be used as the unique suffix when adjusting |
| 356 return dir()->UnlinkEntryFromOrder(kernel_, | 373 // this bookmark's position. Let's make sure it's a valid suffix. |
| 357 write_transaction(), | 374 if (!UniquePosition::IsValidSuffix(tag)) { |
| 358 &lock, | 375 NOTREACHED(); |
| 359 NODE_MANIPULATION); | 376 return; |
| 377 } |
| 378 kernel_->put(UNIQUE_BOOKMARK_TAG, tag); |
| 379 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); |
| 360 } | 380 } |
| 361 | 381 |
| 362 bool MutableEntry::PutPredecessor(const Id& predecessor_id) { | 382 bool MutableEntry::PutPredecessor(const Id& predecessor_id) { |
| 363 if (!UnlinkFromOrder()) | 383 MutableEntry predecessor(write_transaction_, GET_BY_ID, predecessor_id); |
| 384 if (!predecessor.good()) |
| 364 return false; | 385 return false; |
| 365 | 386 dir()->PutPredecessor(kernel_, predecessor.kernel_); |
| 366 if (Get(IS_DEL)) { | |
| 367 DCHECK(predecessor_id.IsNull()); | |
| 368 return true; | |
| 369 } | |
| 370 | |
| 371 // TODO(ncarter): It should be possible to not maintain position for | |
| 372 // non-bookmark items. However, we'd need to robustly handle all possible | |
| 373 // permutations of setting IS_DEL and the SPECIFICS to identify the | |
| 374 // object type; or else, we'd need to add a ModelType to the | |
| 375 // MutableEntry's Create ctor. | |
| 376 // if (!ShouldMaintainPosition()) { | |
| 377 // return false; | |
| 378 // } | |
| 379 | |
| 380 // This is classic insert-into-doubly-linked-list from CS 101 and your last | |
| 381 // job interview. An "IsRoot" Id signifies the head or tail. | |
| 382 Id successor_id; | |
| 383 if (!predecessor_id.IsRoot()) { | |
| 384 MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id); | |
| 385 if (!predecessor.good()) { | |
| 386 LOG(ERROR) << "Predecessor is not good : " | |
| 387 << predecessor_id.GetServerId(); | |
| 388 return false; | |
| 389 } | |
| 390 if (predecessor.Get(PARENT_ID) != Get(PARENT_ID)) | |
| 391 return false; | |
| 392 successor_id = predecessor.Get(NEXT_ID); | |
| 393 predecessor.Put(NEXT_ID, Get(ID)); | |
| 394 } else { | |
| 395 syncable::Directory* dir = trans()->directory(); | |
| 396 if (!dir->GetFirstChildId(trans(), Get(PARENT_ID), &successor_id)) { | |
| 397 return false; | |
| 398 } | |
| 399 } | |
| 400 if (!successor_id.IsRoot()) { | |
| 401 MutableEntry successor(write_transaction(), GET_BY_ID, successor_id); | |
| 402 if (!successor.good()) { | |
| 403 LOG(ERROR) << "Successor is not good: " | |
| 404 << successor_id.GetServerId(); | |
| 405 return false; | |
| 406 } | |
| 407 if (successor.Get(PARENT_ID) != Get(PARENT_ID)) | |
| 408 return false; | |
| 409 successor.Put(PREV_ID, Get(ID)); | |
| 410 } | |
| 411 DCHECK(predecessor_id != Get(ID)); | |
| 412 DCHECK(successor_id != Get(ID)); | |
| 413 Put(PREV_ID, predecessor_id); | |
| 414 Put(NEXT_ID, successor_id); | |
| 415 return true; | 387 return true; |
| 416 } | 388 } |
| 417 | 389 |
| 418 bool MutableEntry::Put(BitTemp field, bool value) { | 390 bool MutableEntry::Put(BitTemp field, bool value) { |
| 419 DCHECK(kernel_); | 391 DCHECK(kernel_); |
| 420 kernel_->put(field, value); | 392 kernel_->put(field, value); |
| 421 return true; | 393 return true; |
| 422 } | 394 } |
| 423 | 395 |
| 424 // This function sets only the flags needed to get this entry to sync. | 396 // This function sets only the flags needed to get this entry to sync. |
| 425 bool MarkForSyncing(MutableEntry* e) { | 397 bool MarkForSyncing(MutableEntry* e) { |
| 426 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); | 398 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); |
| 427 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; | 399 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; |
| 428 if (!(e->Put(IS_UNSYNCED, true))) | 400 if (!(e->Put(IS_UNSYNCED, true))) |
| 429 return false; | 401 return false; |
| 430 e->Put(SYNCING, false); | 402 e->Put(SYNCING, false); |
| 431 return true; | 403 return true; |
| 432 } | 404 } |
| 433 | 405 |
| 434 } // namespace syncable | 406 } // namespace syncable |
| 435 } // namespace syncer | 407 } // namespace syncer |
| OLD | NEW |