mutable_entry.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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
3// found in the LICENSE file.
4
5#include "sync/syncable/mutable_entry.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "sync/internal_api/public/base/node_ordinal.h"
9#include "sync/syncable/directory.h"
10#include "sync/syncable/scoped_index_updater.h"
11#include "sync/syncable/scoped_kernel_lock.h"
12#include "sync/syncable/syncable-inl.h"
13#include "sync/syncable/syncable_changes_version.h"
14#include "sync/syncable/syncable_util.h"
15#include "sync/syncable/write_transaction.h"
16
17using std::string;
18
19namespace syncer {
20namespace syncable {
21
22MutableEntry::MutableEntry(WriteTransaction* trans, Create,
23                           const Id& parent_id, const string& name)
24    : Entry(trans),
25      write_transaction_(trans) {
26  Init(trans, parent_id, name);
27}
28
29
30void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id,
31                        const string& name) {
32  scoped_ptr<EntryKernel> kernel(new EntryKernel);
33  kernel_ = NULL;
34
35  kernel->put(ID, trans->directory_->NextId());
36  kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
37  kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles);
38  kernel->put(PARENT_ID, parent_id);
39  kernel->put(NON_UNIQUE_NAME, name);
40  const base::Time& now = base::Time::Now();
41  kernel->put(CTIME, now);
42  kernel->put(MTIME, now);
43  // We match the database defaults here
44  kernel->put(BASE_VERSION, CHANGES_VERSION);
45  kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal());
46  if (!trans->directory()->InsertEntry(trans, kernel.get())) {
47    return; // We failed inserting, nothing more to do.
48  }
49  // Because this entry is new, it was originally deleted.
50  kernel->put(IS_DEL, true);
51  trans->SaveOriginal(kernel.get());
52  kernel->put(IS_DEL, false);
53
54  // Now swap the pointers.
55  kernel_ = kernel.release();
56}
57
58MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem,
59                           const Id& id)
60    : Entry(trans), write_transaction_(trans) {
61  Entry same_id(trans, GET_BY_ID, id);
62  kernel_ = NULL;
63  if (same_id.good()) {
64    return;  // already have an item with this ID.
65  }
66  scoped_ptr<EntryKernel> kernel(new EntryKernel());
67
68  kernel->put(ID, id);
69  kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
70  kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles);
71  kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal());
72  kernel->put(IS_DEL, true);
73  // We match the database defaults here
74  kernel->put(BASE_VERSION, CHANGES_VERSION);
75  if (!trans->directory()->InsertEntry(trans, kernel.get())) {
76    return;  // Failed inserting.
77  }
78  trans->SaveOriginal(kernel.get());
79
80  kernel_ = kernel.release();
81}
82
83MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id)
84    : Entry(trans, GET_BY_ID, id), write_transaction_(trans) {
85}
86
87MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle,
88                           int64 metahandle)
89    : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) {
90}
91
92MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag,
93                           const std::string& tag)
94    : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) {
95}
96
97MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag,
98                           const string& tag)
99    : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) {
100}
101
102bool MutableEntry::PutIsDel(bool is_del) {
103  DCHECK(kernel_);
104  write_transaction_->SaveOriginal(kernel_);
105  if (is_del == kernel_->ref(IS_DEL)) {
106    return true;
107  }
108  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
114    // need to keep it around.  Unsetting IS_UNSYNCED will:
115    // - Ensure that the item is never committed to the server.
116    // - Allow any items with the same UNIQUE_CLIENT_TAG created on other
117    //   clients to override this entry.
118    // - Let us delete this entry permanently through
119    //   DirectoryBackingStore::DropDeletedEntries() when we next restart sync.
120    //   This will save memory and avoid crbug.com/125381.
121    if (!Get(ID).ServerKnows()) {
122      Put(IS_UNSYNCED, false);
123    }
124  }
125
126  {
127    ScopedKernelLock lock(dir());
128    // Some indices don't include deleted items and must be updated
129    // upon a value change.
130    ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_,
131        dir()->kernel_->parent_id_child_index);
132
133    kernel_->put(IS_DEL, is_del);
134    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
135  }
136
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;
145}
146
147bool MutableEntry::Put(Int64Field field, const int64& value) {
148  DCHECK(kernel_);
149  write_transaction_->SaveOriginal(kernel_);
150  if (kernel_->ref(field) != value) {
151    ScopedKernelLock lock(dir());
152    kernel_->put(field, value);
153    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
154  }
155  return true;
156}
157
158bool MutableEntry::Put(TimeField field, const base::Time& value) {
159  DCHECK(kernel_);
160  write_transaction_->SaveOriginal(kernel_);
161  if (kernel_->ref(field) != value) {
162    kernel_->put(field, value);
163    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
164  }
165  return true;
166}
167
168bool MutableEntry::Put(IdField field, const Id& value) {
169  DCHECK(kernel_);
170  write_transaction_->SaveOriginal(kernel_);
171  if (kernel_->ref(field) != value) {
172    if (ID == field) {
173      if (!dir()->ReindexId(write_transaction(), kernel_, value))
174        return false;
175    } else if (PARENT_ID == field) {
176      PutParentIdPropertyOnly(value);  // Makes sibling order inconsistent.
177      // Fixes up the sibling order inconsistency.
178      if (!PutPredecessor(Id())) {
179        // TODO(lipalani) : Propagate the error to caller. crbug.com/100444.
180        NOTREACHED();
181      }
182    } else {
183      kernel_->put(field, value);
184    }
185    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
186  }
187  return true;
188}
189
190bool MutableEntry::Put(OrdinalField field, const NodeOrdinal& value) {
191  DCHECK(kernel_);
192  DCHECK(value.IsValid());
193  write_transaction_->SaveOriginal(kernel_);
194  if(!kernel_->ref(field).Equals(value)) {
195    ScopedKernelLock lock(dir());
196    if (SERVER_ORDINAL_IN_PARENT == field) {
197      ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(
198          lock, kernel_, dir()->kernel_->parent_id_child_index);
199      kernel_->put(field, value);
200    } else {
201      kernel_->put(field, value);
202    }
203    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
204  }
205  return true;
206}
207
208void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
209  write_transaction_->SaveOriginal(kernel_);
210  dir()->ReindexParentId(write_transaction(), kernel_, parent_id);
211  kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
212}
213
214bool MutableEntry::Put(BaseVersion field, int64 value) {
215  DCHECK(kernel_);
216  write_transaction_->SaveOriginal(kernel_);
217  if (kernel_->ref(field) != value) {
218    kernel_->put(field, value);
219    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
220  }
221  return true;
222}
223
224bool MutableEntry::Put(StringField field, const string& value) {
225  DCHECK(kernel_);
226  write_transaction_->SaveOriginal(kernel_);
227  if (field == UNIQUE_CLIENT_TAG) {
228    return PutUniqueClientTag(value);
229  }
230
231  if (kernel_->ref(field) != value) {
232    kernel_->put(field, value);
233    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
234  }
235  return true;
236}
237
238bool MutableEntry::Put(ProtoField field,
239                       const sync_pb::EntitySpecifics& value) {
240  DCHECK(kernel_);
241  write_transaction_->SaveOriginal(kernel_);
242  // TODO(ncarter): This is unfortunately heavyweight.  Can we do
243  // better?
244  if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) {
245    const bool update_unapplied_updates_index =
246        (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE);
247    if (update_unapplied_updates_index) {
248      // Remove ourselves from unapplied_update_metahandles with our
249      // old server type.
250      const ModelType old_server_type = kernel_->GetServerModelType();
251      const int64 metahandle = kernel_->ref(META_HANDLE);
252      size_t erase_count =
253          dir()->kernel_->unapplied_update_metahandles[old_server_type]
254          .erase(metahandle);
255      DCHECK_EQ(erase_count, 1u);
256    }
257
258    kernel_->put(field, value);
259    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
260
261    if (update_unapplied_updates_index) {
262      // Add ourselves back into unapplied_update_metahandles with our
263      // new server type.
264      const ModelType new_server_type = kernel_->GetServerModelType();
265      const int64 metahandle = kernel_->ref(META_HANDLE);
266      dir()->kernel_->unapplied_update_metahandles[new_server_type]
267          .insert(metahandle);
268    }
269  }
270  return true;
271}
272
273bool MutableEntry::Put(BitField field, bool value) {
274  DCHECK(kernel_);
275  write_transaction_->SaveOriginal(kernel_);
276  if (kernel_->ref(field) != value) {
277    kernel_->put(field, value);
278    kernel_->mark_dirty(GetDirtyIndexHelper());
279  }
280  return true;
281}
282
283MetahandleSet* MutableEntry::GetDirtyIndexHelper() {
284  return dir()->kernel_->dirty_metahandles;
285}
286
287bool MutableEntry::PutUniqueClientTag(const string& new_tag) {
288  write_transaction_->SaveOriginal(kernel_);
289  // There is no SERVER_UNIQUE_CLIENT_TAG. This field is similar to ID.
290  string old_tag = kernel_->ref(UNIQUE_CLIENT_TAG);
291  if (old_tag == new_tag) {
292    return true;
293  }
294
295  ScopedKernelLock lock(dir());
296  if (!new_tag.empty()) {
297    // Make sure your new value is not in there already.
298    EntryKernel lookup_kernel_ = *kernel_;
299    lookup_kernel_.put(UNIQUE_CLIENT_TAG, new_tag);
300    bool new_tag_conflicts =
301        (dir()->kernel_->client_tag_index->count(&lookup_kernel_) > 0);
302    if (new_tag_conflicts) {
303      return false;
304    }
305  }
306
307  {
308    ScopedIndexUpdater<ClientTagIndexer> index_updater(lock, kernel_,
309        dir()->kernel_->client_tag_index);
310    kernel_->put(UNIQUE_CLIENT_TAG, new_tag);
311    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
312  }
313  return true;
314}
315
316bool MutableEntry::Put(IndexedBitField field, bool value) {
317  DCHECK(kernel_);
318  write_transaction_->SaveOriginal(kernel_);
319  if (kernel_->ref(field) != value) {
320    MetahandleSet* index;
321    if (IS_UNSYNCED == field) {
322      index = dir()->kernel_->unsynced_metahandles;
323    } else {
324      // Use kernel_->GetServerModelType() instead of
325      // GetServerModelType() as we may trigger some DCHECKs in the
326      // latter.
327      index =
328          &dir()->kernel_->unapplied_update_metahandles[
329              kernel_->GetServerModelType()];
330    }
331
332    ScopedKernelLock lock(dir());
333    if (value) {
334      if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
335                      FROM_HERE,
336                      "Could not insert",
337                      write_transaction())) {
338        return false;
339      }
340    } else {
341      if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
342                      FROM_HERE,
343                      "Entry Not succesfully erased",
344                      write_transaction())) {
345        return false;
346      }
347    }
348    kernel_->put(field, value);
349    kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
350  }
351  return true;
352}
353
354bool MutableEntry::UnlinkFromOrder() {
355  ScopedKernelLock lock(dir());
356  return dir()->UnlinkEntryFromOrder(kernel_,
357                                     write_transaction(),
358                                     &lock,
359                                     NODE_MANIPULATION);
360}
361
362bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
363  if (!UnlinkFromOrder())
364    return false;
365
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;
416}
417
418bool MutableEntry::Put(BitTemp field, bool value) {
419  DCHECK(kernel_);
420  kernel_->put(field, value);
421  return true;
422}
423
424// This function sets only the flags needed to get this entry to sync.
425bool MarkForSyncing(MutableEntry* e) {
426  DCHECK_NE(static_cast<MutableEntry*>(NULL), e);
427  DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing.";
428  if (!(e->Put(IS_UNSYNCED, true)))
429    return false;
430  e->Put(SYNCING, false);
431  return true;
432}
433
434}  // namespace syncable
435}  // namespace syncer
436