1// Copyright 2013 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/model_neutral_mutable_entry.h"
6
7#include <string>
8
9#include "sync/internal_api/public/base/unique_position.h"
10#include "sync/syncable/directory.h"
11#include "sync/syncable/scoped_kernel_lock.h"
12#include "sync/syncable/syncable_changes_version.h"
13#include "sync/syncable/syncable_util.h"
14#include "sync/syncable/syncable_write_transaction.h"
15
16using std::string;
17
18namespace syncer {
19
20namespace syncable {
21
22ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans,
23                                                   CreateNewUpdateItem,
24                                                   const Id& id)
25    : Entry(trans), base_write_transaction_(trans) {
26  Entry same_id(trans, GET_BY_ID, id);
27  kernel_ = NULL;
28  if (same_id.good()) {
29    return;  // already have an item with this ID.
30  }
31  scoped_ptr<EntryKernel> kernel(new EntryKernel());
32
33  kernel->put(ID, id);
34  kernel->put(META_HANDLE, trans->directory()->NextMetahandle());
35  kernel->mark_dirty(&trans->directory()->kernel_->dirty_metahandles);
36  kernel->put(IS_DEL, true);
37  // We match the database defaults here
38  kernel->put(BASE_VERSION, CHANGES_VERSION);
39  if (!trans->directory()->InsertEntry(trans, kernel.get())) {
40    return;  // Failed inserting.
41  }
42  trans->TrackChangesTo(kernel.get());
43
44  kernel_ = kernel.release();
45}
46
47ModelNeutralMutableEntry::ModelNeutralMutableEntry(
48    BaseWriteTransaction* trans, GetById, const Id& id)
49    : Entry(trans, GET_BY_ID, id), base_write_transaction_(trans) {
50}
51
52ModelNeutralMutableEntry::ModelNeutralMutableEntry(
53    BaseWriteTransaction* trans, GetByHandle, int64 metahandle)
54    : Entry(trans, GET_BY_HANDLE, metahandle), base_write_transaction_(trans) {
55}
56
57ModelNeutralMutableEntry::ModelNeutralMutableEntry(
58    BaseWriteTransaction* trans, GetByClientTag, const std::string& tag)
59    : Entry(trans, GET_BY_CLIENT_TAG, tag), base_write_transaction_(trans) {
60}
61
62ModelNeutralMutableEntry::ModelNeutralMutableEntry(
63    BaseWriteTransaction* trans, GetTypeRoot, ModelType type)
64    : Entry(trans, GET_TYPE_ROOT, type), base_write_transaction_(trans) {
65}
66
67void ModelNeutralMutableEntry::PutBaseVersion(int64 value) {
68  DCHECK(kernel_);
69  base_write_transaction_->TrackChangesTo(kernel_);
70  if (kernel_->ref(BASE_VERSION) != value) {
71    kernel_->put(BASE_VERSION, value);
72    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
73  }
74}
75
76void ModelNeutralMutableEntry::PutServerVersion(int64 value) {
77  DCHECK(kernel_);
78  base_write_transaction_->TrackChangesTo(kernel_);
79  if (kernel_->ref(SERVER_VERSION) != value) {
80    ScopedKernelLock lock(dir());
81    kernel_->put(SERVER_VERSION, value);
82    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
83  }
84}
85
86void ModelNeutralMutableEntry::PutServerMtime(base::Time value) {
87  DCHECK(kernel_);
88  base_write_transaction_->TrackChangesTo(kernel_);
89  if (kernel_->ref(SERVER_MTIME) != value) {
90    kernel_->put(SERVER_MTIME, value);
91    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
92  }
93}
94
95void ModelNeutralMutableEntry::PutServerCtime(base::Time value) {
96  DCHECK(kernel_);
97  base_write_transaction_->TrackChangesTo(kernel_);
98  if (kernel_->ref(SERVER_CTIME) != value) {
99    kernel_->put(SERVER_CTIME, value);
100    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
101  }
102}
103
104bool ModelNeutralMutableEntry::PutId(const Id& value) {
105  DCHECK(kernel_);
106  base_write_transaction_->TrackChangesTo(kernel_);
107  if (kernel_->ref(ID) != value) {
108    if (!dir()->ReindexId(base_write_transaction(), kernel_, value))
109      return false;
110    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
111  }
112  return true;
113}
114
115void ModelNeutralMutableEntry::PutServerParentId(const Id& value) {
116  DCHECK(kernel_);
117  base_write_transaction_->TrackChangesTo(kernel_);
118
119  if (kernel_->ref(SERVER_PARENT_ID) != value) {
120    kernel_->put(SERVER_PARENT_ID, value);
121    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
122  }
123}
124
125bool ModelNeutralMutableEntry::PutIsUnsynced(bool value) {
126  DCHECK(kernel_);
127  base_write_transaction_->TrackChangesTo(kernel_);
128  if (kernel_->ref(IS_UNSYNCED) != value) {
129    MetahandleSet* index = &dir()->kernel_->unsynced_metahandles;
130
131    ScopedKernelLock lock(dir());
132    if (value) {
133      if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
134                      FROM_HERE,
135                      "Could not insert",
136                      base_write_transaction())) {
137        return false;
138      }
139    } else {
140      if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
141                      FROM_HERE,
142                      "Entry Not succesfully erased",
143                      base_write_transaction())) {
144        return false;
145      }
146    }
147    kernel_->put(IS_UNSYNCED, value);
148    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
149  }
150  return true;
151}
152
153bool ModelNeutralMutableEntry::PutIsUnappliedUpdate(bool value) {
154  DCHECK(kernel_);
155  base_write_transaction_->TrackChangesTo(kernel_);
156  if (kernel_->ref(IS_UNAPPLIED_UPDATE) != value) {
157    // Use kernel_->GetServerModelType() instead of
158    // GetServerModelType() as we may trigger some DCHECKs in the
159    // latter.
160    MetahandleSet* index = &dir()->kernel_->unapplied_update_metahandles[
161        kernel_->GetServerModelType()];
162
163    ScopedKernelLock lock(dir());
164    if (value) {
165      if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
166                      FROM_HERE,
167                      "Could not insert",
168                      base_write_transaction())) {
169        return false;
170      }
171    } else {
172      if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
173                      FROM_HERE,
174                      "Entry Not succesfully erased",
175                      base_write_transaction())) {
176        return false;
177      }
178    }
179    kernel_->put(IS_UNAPPLIED_UPDATE, value);
180    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
181  }
182  return true;
183}
184
185void ModelNeutralMutableEntry::PutServerIsDir(bool value) {
186  DCHECK(kernel_);
187  base_write_transaction_->TrackChangesTo(kernel_);
188  bool old_value = kernel_->ref(SERVER_IS_DIR);
189  if (old_value != value) {
190    kernel_->put(SERVER_IS_DIR, value);
191    kernel_->mark_dirty(GetDirtyIndexHelper());
192  }
193}
194
195void ModelNeutralMutableEntry::PutServerIsDel(bool value) {
196  DCHECK(kernel_);
197  base_write_transaction_->TrackChangesTo(kernel_);
198  bool old_value = kernel_->ref(SERVER_IS_DEL);
199  if (old_value != value) {
200    kernel_->put(SERVER_IS_DEL, value);
201    kernel_->mark_dirty(GetDirtyIndexHelper());
202  }
203
204  // Update delete journal for existence status change on server side here
205  // instead of in PutIsDel() because IS_DEL may not be updated due to
206  // early returns when processing updates. And because
207  // UpdateDeleteJournalForServerDelete() checks for SERVER_IS_DEL, it has
208  // to be called on sync thread.
209  dir()->delete_journal()->UpdateDeleteJournalForServerDelete(
210      base_write_transaction(), old_value, *kernel_);
211}
212
213void ModelNeutralMutableEntry::PutServerNonUniqueName(
214    const std::string& value) {
215  DCHECK(kernel_);
216  base_write_transaction_->TrackChangesTo(kernel_);
217
218  if (kernel_->ref(SERVER_NON_UNIQUE_NAME) != value) {
219    kernel_->put(SERVER_NON_UNIQUE_NAME, value);
220    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
221  }
222}
223
224bool ModelNeutralMutableEntry::PutUniqueServerTag(const string& new_tag) {
225  if (new_tag == kernel_->ref(UNIQUE_SERVER_TAG)) {
226    return true;
227  }
228
229  base_write_transaction_->TrackChangesTo(kernel_);
230  ScopedKernelLock lock(dir());
231  // Make sure your new value is not in there already.
232  if (dir()->kernel_->server_tags_map.find(new_tag) !=
233      dir()->kernel_->server_tags_map.end()) {
234    DVLOG(1) << "Detected duplicate server tag";
235    return false;
236  }
237  dir()->kernel_->server_tags_map.erase(
238      kernel_->ref(UNIQUE_SERVER_TAG));
239  kernel_->put(UNIQUE_SERVER_TAG, new_tag);
240  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
241  if (!new_tag.empty()) {
242    dir()->kernel_->server_tags_map[new_tag] = kernel_;
243  }
244
245  return true;
246}
247
248bool ModelNeutralMutableEntry::PutUniqueClientTag(const string& new_tag) {
249  if (new_tag == kernel_->ref(UNIQUE_CLIENT_TAG)) {
250    return true;
251  }
252
253  base_write_transaction_->TrackChangesTo(kernel_);
254  ScopedKernelLock lock(dir());
255  // Make sure your new value is not in there already.
256  if (dir()->kernel_->client_tags_map.find(new_tag) !=
257      dir()->kernel_->client_tags_map.end()) {
258    DVLOG(1) << "Detected duplicate client tag";
259    return false;
260  }
261  dir()->kernel_->client_tags_map.erase(
262      kernel_->ref(UNIQUE_CLIENT_TAG));
263  kernel_->put(UNIQUE_CLIENT_TAG, new_tag);
264  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
265  if (!new_tag.empty()) {
266    dir()->kernel_->client_tags_map[new_tag] = kernel_;
267  }
268
269  return true;
270}
271
272void ModelNeutralMutableEntry::PutUniqueBookmarkTag(const std::string& tag) {
273  // This unique tag will eventually be used as the unique suffix when adjusting
274  // this bookmark's position.  Let's make sure it's a valid suffix.
275  if (!UniquePosition::IsValidSuffix(tag)) {
276    NOTREACHED();
277    return;
278  }
279
280  if (!kernel_->ref(UNIQUE_BOOKMARK_TAG).empty() &&
281      tag != kernel_->ref(UNIQUE_BOOKMARK_TAG)) {
282    // There is only one scenario where our tag is expected to change.  That
283    // scenario occurs when our current tag is a non-correct tag assigned during
284    // the UniquePosition migration.
285    std::string migration_generated_tag =
286        GenerateSyncableBookmarkHash(std::string(),
287                                     kernel_->ref(ID).GetServerId());
288    DCHECK_EQ(migration_generated_tag, kernel_->ref(UNIQUE_BOOKMARK_TAG));
289  }
290
291  kernel_->put(UNIQUE_BOOKMARK_TAG, tag);
292  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
293}
294
295void ModelNeutralMutableEntry::PutServerSpecifics(
296    const sync_pb::EntitySpecifics& value) {
297  DCHECK(kernel_);
298  CHECK(!value.password().has_client_only_encrypted_data());
299  base_write_transaction_->TrackChangesTo(kernel_);
300  // TODO(ncarter): This is unfortunately heavyweight.  Can we do
301  // better?
302  if (kernel_->ref(SERVER_SPECIFICS).SerializeAsString() !=
303      value.SerializeAsString()) {
304    if (kernel_->ref(IS_UNAPPLIED_UPDATE)) {
305      // Remove ourselves from unapplied_update_metahandles with our
306      // old server type.
307      const ModelType old_server_type = kernel_->GetServerModelType();
308      const int64 metahandle = kernel_->ref(META_HANDLE);
309      size_t erase_count =
310          dir()->kernel_->unapplied_update_metahandles[old_server_type]
311          .erase(metahandle);
312      DCHECK_EQ(erase_count, 1u);
313    }
314
315    kernel_->put(SERVER_SPECIFICS, value);
316    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
317
318    if (kernel_->ref(IS_UNAPPLIED_UPDATE)) {
319      // Add ourselves back into unapplied_update_metahandles with our
320      // new server type.
321      const ModelType new_server_type = kernel_->GetServerModelType();
322      const int64 metahandle = kernel_->ref(META_HANDLE);
323      dir()->kernel_->unapplied_update_metahandles[new_server_type]
324          .insert(metahandle);
325    }
326  }
327}
328
329void ModelNeutralMutableEntry::PutBaseServerSpecifics(
330    const sync_pb::EntitySpecifics& value) {
331  DCHECK(kernel_);
332  CHECK(!value.password().has_client_only_encrypted_data());
333  base_write_transaction_->TrackChangesTo(kernel_);
334  // TODO(ncarter): This is unfortunately heavyweight.  Can we do
335  // better?
336  if (kernel_->ref(BASE_SERVER_SPECIFICS).SerializeAsString()
337      != value.SerializeAsString()) {
338    kernel_->put(BASE_SERVER_SPECIFICS, value);
339    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
340  }
341}
342
343void ModelNeutralMutableEntry::PutServerUniquePosition(
344    const UniquePosition& value) {
345  DCHECK(kernel_);
346  base_write_transaction_->TrackChangesTo(kernel_);
347  if(!kernel_->ref(SERVER_UNIQUE_POSITION).Equals(value)) {
348    // We should never overwrite a valid position with an invalid one.
349    DCHECK(value.IsValid());
350    ScopedKernelLock lock(dir());
351    kernel_->put(SERVER_UNIQUE_POSITION, value);
352    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
353  }
354}
355
356void ModelNeutralMutableEntry::PutServerAttachmentMetadata(
357    const sync_pb::AttachmentMetadata& value) {
358  DCHECK(kernel_);
359  base_write_transaction_->TrackChangesTo(kernel_);
360
361  if (kernel_->ref(SERVER_ATTACHMENT_METADATA).SerializeAsString() !=
362      value.SerializeAsString()) {
363    kernel_->put(SERVER_ATTACHMENT_METADATA, value);
364    kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
365  }
366}
367
368void ModelNeutralMutableEntry::PutSyncing(bool value) {
369  kernel_->put(SYNCING, value);
370}
371
372void ModelNeutralMutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
373  base_write_transaction_->TrackChangesTo(kernel_);
374  dir()->ReindexParentId(base_write_transaction(), kernel_, parent_id);
375  kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
376}
377
378void ModelNeutralMutableEntry::UpdateTransactionVersion(int64 value) {
379  ScopedKernelLock lock(dir());
380  kernel_->put(TRANSACTION_VERSION, value);
381  kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles));
382}
383
384ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans)
385  : Entry(trans), base_write_transaction_(trans) {}
386
387MetahandleSet* ModelNeutralMutableEntry::GetDirtyIndexHelper() {
388  return &dir()->kernel_->dirty_metahandles;
389}
390
391}  // namespace syncable
392
393}  // namespace syncer
394