mutable_entry.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright 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/unique_position.h"
9#include "sync/syncable/directory.h"
10#include "sync/syncable/scoped_kernel_lock.h"
11#include "sync/syncable/scoped_parent_child_index_updater.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/syncable_write_transaction.h"
16
17using std::string;
18
19namespace syncer {
20namespace syncable {
21
22void MutableEntry::Init(WriteTransaction* trans,
23                        ModelType model_type,
24                        const Id& parent_id,
25                        const string& name) {
26  scoped_ptr<EntryKernel> kernel(new EntryKernel);
27  kernel_ = NULL;
28
29  kernel->put(ID, trans->directory_->NextId());
30  kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
31  kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles);
32  kernel->put(PARENT_ID, parent_id);
33  kernel->put(NON_UNIQUE_NAME, name);
34  const base::Time& now = base::Time::Now();
35  kernel->put(CTIME, now);
36  kernel->put(MTIME, now);
37  // We match the database defaults here
38  kernel->put(BASE_VERSION, CHANGES_VERSION);
39
40  // Normally the SPECIFICS setting code is wrapped in logic to deal with
41  // unknown fields and encryption.  Since all we want to do here is ensure that
42  // GetModelType() returns a correct value from the very beginning, these
43  // few lines are sufficient.
44  sync_pb::EntitySpecifics specifics;
45  AddDefaultFieldValue(model_type, &specifics);
46  kernel->put(SPECIFICS, specifics);
47
48  // Because this entry is new, it was originally deleted.
49  kernel->put(IS_DEL, true);
50  trans->SaveOriginal(kernel.get());
51  kernel->put(IS_DEL, false);
52
53  // Now swap the pointers.
54  kernel_ = kernel.release();
55}
56
57MutableEntry::MutableEntry(WriteTransaction* trans,
58                           Create,
59                           ModelType model_type,
60                           const Id& parent_id,
61                           const string& name)
62    : Entry(trans),
63      write_transaction_(trans) {
64  Init(trans, model_type, parent_id, name);
65  // We need to have a valid position ready before we can index the item.
66  if (model_type == BOOKMARKS) {
67    // Base the tag off of our cache-guid and local "c-" style ID.
68    std::string unique_tag = syncable::GenerateSyncableBookmarkHash(
69        trans->directory()->cache_guid(), Get(ID).GetServerId());
70    kernel_->put(UNIQUE_BOOKMARK_TAG, unique_tag);
71    kernel_->put(UNIQUE_POSITION, UniquePosition::InitialPosition(unique_tag));
72  } else {
73    DCHECK(!ShouldMaintainPosition());
74  }
75
76  bool result = trans->directory()->InsertEntry(trans, kernel_);
77  DCHECK(result);
78}
79
80MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem,
81                           const Id& id)
82    : Entry(trans), write_transaction_(trans) {
83  Entry same_id(trans, GET_BY_ID, id);
84  kernel_ = NULL;
85  if (same_id.good()) {
86    return;  // already have an item with this ID.
87  }
88  scoped_ptr<EntryKernel> kernel(new EntryKernel());
89
90  kernel->put(ID, id);
91  kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
92  kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles);
93  kernel->put(IS_DEL, true);
94  // We match the database defaults here
95  kernel->put(BASE_VERSION, CHANGES_VERSION);
96  if (!trans->directory()->InsertEntry(trans, kernel.get())) {
97    return;  // Failed inserting.
98  }
99  trans->SaveOriginal(kernel.get());
100
101  kernel_ = kernel.release();
102}
103
104MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id)
105    : Entry(trans, GET_BY_ID, id), write_transaction_(trans) {
106}
107
108MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle,
109                           int64 metahandle)
110    : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) {
111}
112
113MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag,
114                           const std::string& tag)
115    : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) {
116}
117
118MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag,
119                           const string& tag)
120    : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) {
121}
122
123bool MutableEntry::PutIsDel(bool is_del) {
124  DCHECK(kernel_);
125  write_transaction_->SaveOriginal(kernel_);
126  if (is_del == kernel_->ref(IS_DEL)) {
127    return true;
128  }
129  if (is_del) {
130    // If the server never knew about this item and it's deleted then we don't
131    // need to keep it around.  Unsetting IS_UNSYNCED will:
132    // - Ensure that the item is never committed to the server.
133    // - Allow any items with the same UNIQUE_CLIENT_TAG created on other
134    //   clients to override this entry.
135    // - Let us delete this entry permanently through
136    //   DirectoryBackingStore::DropDeletedEntries() when we next restart sync.
137    //   This will save memory and avoid crbug.com/125381.
138    if (!Get(ID).ServerKnows()) {
139      Put(IS_UNSYNCED, false);
140    }
141  }
142
143  {
144    ScopedKernelLock lock(dir());
145    // Some indices don't include deleted items and must be updated
146    // upon a value change.
147    ScopedParentChildIndexUpdater updater(lock, kernel_,
148        &dir()->kernel_->parent_child_index);
149
150    kernel_->put(IS_DEL, is_del);
151    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
152  }
153
154  return true;
155}
156
157bool MutableEntry::Put(Int64Field field, const int64& value) {
158  DCHECK(kernel_);
159
160  // We shouldn't set TRANSACTION_VERSION here.  See UpdateTransactionVersion.
161  DCHECK_NE(TRANSACTION_VERSION, field);
162
163  write_transaction_->SaveOriginal(kernel_);
164  if (kernel_->ref(field) != value) {
165    ScopedKernelLock lock(dir());
166    kernel_->put(field, value);
167    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
168  }
169  return true;
170}
171
172bool MutableEntry::Put(TimeField field, const base::Time& value) {
173  DCHECK(kernel_);
174  write_transaction_->SaveOriginal(kernel_);
175  if (kernel_->ref(field) != value) {
176    kernel_->put(field, value);
177    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
178  }
179  return true;
180}
181
182bool MutableEntry::Put(IdField field, const Id& value) {
183  DCHECK(kernel_);
184  write_transaction_->SaveOriginal(kernel_);
185  if (kernel_->ref(field) != value) {
186    if (ID == field) {
187      if (!dir()->ReindexId(write_transaction(), kernel_, value))
188        return false;
189    } else if (PARENT_ID == field) {
190      PutParentIdPropertyOnly(value);
191      if (!Get(IS_DEL)) {
192        if (!PutPredecessor(Id())) {
193          // TODO(lipalani) : Propagate the error to caller. crbug.com/100444.
194          NOTREACHED();
195        }
196      }
197    } else {
198      kernel_->put(field, value);
199    }
200    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
201  }
202  return true;
203}
204
205bool MutableEntry::Put(UniquePositionField field, const UniquePosition& value) {
206  DCHECK(kernel_);
207  write_transaction_->SaveOriginal(kernel_);
208  if(!kernel_->ref(field).Equals(value)) {
209    // We should never overwrite a valid position with an invalid one.
210    DCHECK(value.IsValid());
211    ScopedKernelLock lock(dir());
212    if (UNIQUE_POSITION == field) {
213      ScopedParentChildIndexUpdater updater(
214          lock, kernel_, &dir()->kernel_->parent_child_index);
215      kernel_->put(field, value);
216    } else {
217      kernel_->put(field, value);
218    }
219    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
220  }
221  return true;
222}
223
224void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
225  write_transaction_->SaveOriginal(kernel_);
226  dir()->ReindexParentId(write_transaction(), kernel_, parent_id);
227  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
228}
229
230bool MutableEntry::Put(BaseVersion field, int64 value) {
231  DCHECK(kernel_);
232  write_transaction_->SaveOriginal(kernel_);
233  if (kernel_->ref(field) != value) {
234    kernel_->put(field, value);
235    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
236  }
237  return true;
238}
239
240bool MutableEntry::Put(StringField field, const string& value) {
241  DCHECK(kernel_);
242  write_transaction_->SaveOriginal(kernel_);
243  if (field == UNIQUE_CLIENT_TAG) {
244    return PutUniqueClientTag(value);
245  }
246
247  if (field == UNIQUE_SERVER_TAG) {
248    return PutUniqueServerTag(value);
249  }
250
251  DCHECK_NE(UNIQUE_BOOKMARK_TAG, field)
252      << "Should use PutUniqueBookmarkTag instead of Put(UNIQUE_BOOKMARK_TAG)";
253
254  if (kernel_->ref(field) != value) {
255    kernel_->put(field, value);
256    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
257  }
258  return true;
259}
260
261bool MutableEntry::Put(ProtoField field,
262                       const sync_pb::EntitySpecifics& value) {
263  DCHECK(kernel_);
264  write_transaction_->SaveOriginal(kernel_);
265  // TODO(ncarter): This is unfortunately heavyweight.  Can we do
266  // better?
267  if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) {
268    const bool update_unapplied_updates_index =
269        (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE);
270    if (update_unapplied_updates_index) {
271      // Remove ourselves from unapplied_update_metahandles with our
272      // old server type.
273      const ModelType old_server_type = kernel_->GetServerModelType();
274      const int64 metahandle = kernel_->ref(META_HANDLE);
275      size_t erase_count =
276          dir()->kernel_->unapplied_update_metahandles[old_server_type]
277          .erase(metahandle);
278      DCHECK_EQ(erase_count, 1u);
279    }
280
281    kernel_->put(field, value);
282    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
283
284    if (update_unapplied_updates_index) {
285      // Add ourselves back into unapplied_update_metahandles with our
286      // new server type.
287      const ModelType new_server_type = kernel_->GetServerModelType();
288      const int64 metahandle = kernel_->ref(META_HANDLE);
289      dir()->kernel_->unapplied_update_metahandles[new_server_type]
290          .insert(metahandle);
291    }
292  }
293  return true;
294}
295
296bool MutableEntry::Put(BitField field, bool value) {
297  DCHECK(kernel_);
298  write_transaction_->SaveOriginal(kernel_);
299  bool old_value = kernel_->ref(field);
300  if (old_value != value) {
301    kernel_->put(field, value);
302    kernel_->mark_dirty(GetDirtyIndexHelper());
303  }
304
305  // Update delete journal for existence status change on server side here
306  // instead of in PutIsDel() because IS_DEL may not be updated due to
307  // early returns when processing updates. And because
308  // UpdateDeleteJournalForServerDelete() checks for SERVER_IS_DEL, it has
309  // to be called on sync thread.
310  if (field == SERVER_IS_DEL) {
311    dir()->delete_journal()->UpdateDeleteJournalForServerDelete(
312        write_transaction(), old_value, *kernel_);
313  }
314
315  return true;
316}
317
318MetahandleSet* MutableEntry::GetDirtyIndexHelper() {
319  return &dir()->kernel_->dirty_metahandles;
320}
321
322bool MutableEntry::PutUniqueClientTag(const string& new_tag) {
323  if (new_tag == kernel_->ref(UNIQUE_CLIENT_TAG)) {
324    return true;
325  }
326
327  write_transaction_->SaveOriginal(kernel_);
328  ScopedKernelLock lock(dir());
329  // Make sure your new value is not in there already.
330  if (dir()->kernel_->client_tags_map.find(new_tag) !=
331      dir()->kernel_->client_tags_map.end()) {
332    DVLOG(1) << "Detected duplicate client tag";
333    return false;
334  }
335  dir()->kernel_->client_tags_map.erase(
336      kernel_->ref(UNIQUE_CLIENT_TAG));
337  kernel_->put(UNIQUE_CLIENT_TAG, new_tag);
338  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
339  if (!new_tag.empty()) {
340    dir()->kernel_->client_tags_map[new_tag] = kernel_;
341  }
342
343  return true;
344}
345
346bool MutableEntry::PutUniqueServerTag(const string& new_tag) {
347  if (new_tag == kernel_->ref(UNIQUE_SERVER_TAG)) {
348    return true;
349  }
350
351  write_transaction_->SaveOriginal(kernel_);
352  ScopedKernelLock lock(dir());
353  // Make sure your new value is not in there already.
354  if (dir()->kernel_->server_tags_map.find(new_tag) !=
355      dir()->kernel_->server_tags_map.end()) {
356    DVLOG(1) << "Detected duplicate server tag";
357    return false;
358  }
359  dir()->kernel_->server_tags_map.erase(
360      kernel_->ref(UNIQUE_SERVER_TAG));
361  kernel_->put(UNIQUE_SERVER_TAG, new_tag);
362  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
363  if (!new_tag.empty()) {
364    dir()->kernel_->server_tags_map[new_tag] = kernel_;
365  }
366
367  return true;
368}
369
370bool MutableEntry::Put(IndexedBitField field, bool value) {
371  DCHECK(kernel_);
372  write_transaction_->SaveOriginal(kernel_);
373  if (kernel_->ref(field) != value) {
374    MetahandleSet* index;
375    if (IS_UNSYNCED == field) {
376      index = &dir()->kernel_->unsynced_metahandles;
377    } else {
378      // Use kernel_->GetServerModelType() instead of
379      // GetServerModelType() as we may trigger some DCHECKs in the
380      // latter.
381      index =
382          &dir()->kernel_->unapplied_update_metahandles[
383              kernel_->GetServerModelType()];
384    }
385
386    ScopedKernelLock lock(dir());
387    if (value) {
388      if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
389                      FROM_HERE,
390                      "Could not insert",
391                      write_transaction())) {
392        return false;
393      }
394    } else {
395      if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
396                      FROM_HERE,
397                      "Entry Not succesfully erased",
398                      write_transaction())) {
399        return false;
400      }
401    }
402    kernel_->put(field, value);
403    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
404  }
405  return true;
406}
407
408void MutableEntry::PutUniqueBookmarkTag(const std::string& tag) {
409  // This unique tag will eventually be used as the unique suffix when adjusting
410  // this bookmark's position.  Let's make sure it's a valid suffix.
411  if (!UniquePosition::IsValidSuffix(tag)) {
412    NOTREACHED();
413    return;
414  }
415
416  if (!kernel_->ref(UNIQUE_BOOKMARK_TAG).empty()
417      && tag != kernel_->ref(UNIQUE_BOOKMARK_TAG)) {
418    // There is only one scenario where our tag is expected to change.  That
419    // scenario occurs when our current tag is a non-correct tag assigned during
420    // the UniquePosition migration.
421    std::string migration_generated_tag =
422        GenerateSyncableBookmarkHash(std::string(),
423                                     kernel_->ref(ID).GetServerId());
424    DCHECK_EQ(migration_generated_tag, kernel_->ref(UNIQUE_BOOKMARK_TAG));
425  }
426
427  kernel_->put(UNIQUE_BOOKMARK_TAG, tag);
428  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
429}
430
431bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
432  MutableEntry predecessor(write_transaction_, GET_BY_ID, predecessor_id);
433  if (!predecessor.good())
434    return false;
435  dir()->PutPredecessor(kernel_, predecessor.kernel_);
436  return true;
437}
438
439bool MutableEntry::Put(BitTemp field, bool value) {
440  DCHECK(kernel_);
441  kernel_->put(field, value);
442  return true;
443}
444
445void MutableEntry::UpdateTransactionVersion(int64 value) {
446  ScopedKernelLock lock(dir());
447  kernel_->put(TRANSACTION_VERSION, value);
448  kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles));
449}
450
451// This function sets only the flags needed to get this entry to sync.
452bool MarkForSyncing(MutableEntry* e) {
453  DCHECK_NE(static_cast<MutableEntry*>(NULL), e);
454  DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing.";
455  if (!(e->Put(IS_UNSYNCED, true)))
456    return false;
457  e->Put(SYNCING, false);
458  return true;
459}
460
461}  // namespace syncable
462}  // namespace syncer
463