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 |