Home | History | Annotate | Download | only in syncable
      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 
     16 using std::string;
     17 
     18 namespace syncer {
     19 
     20 namespace syncable {
     21 
     22 ModelNeutralMutableEntry::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 
     47 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
     48     BaseWriteTransaction* trans, GetById, const Id& id)
     49     : Entry(trans, GET_BY_ID, id), base_write_transaction_(trans) {
     50 }
     51 
     52 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
     53     BaseWriteTransaction* trans, GetByHandle, int64 metahandle)
     54     : Entry(trans, GET_BY_HANDLE, metahandle), base_write_transaction_(trans) {
     55 }
     56 
     57 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
     58     BaseWriteTransaction* trans, GetByClientTag, const std::string& tag)
     59     : Entry(trans, GET_BY_CLIENT_TAG, tag), base_write_transaction_(trans) {
     60 }
     61 
     62 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
     63     BaseWriteTransaction* trans, GetTypeRoot, ModelType type)
     64     : Entry(trans, GET_TYPE_ROOT, type), base_write_transaction_(trans) {
     65 }
     66 
     67 void 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 
     76 void 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 
     86 void 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 
     95 void 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 
    104 bool 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 
    115 void 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 
    125 bool 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 
    153 bool 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 
    185 void 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 
    195 void 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 
    213 void 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 
    224 bool 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 
    248 bool 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 
    272 void 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 
    295 void 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 
    329 void 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 
    343 void 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 
    356 void ModelNeutralMutableEntry::PutSyncing(bool value) {
    357   kernel_->put(SYNCING, value);
    358 }
    359 
    360 void ModelNeutralMutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
    361   base_write_transaction_->TrackChangesTo(kernel_);
    362   dir()->ReindexParentId(base_write_transaction(), kernel_, parent_id);
    363   kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    364 }
    365 
    366 void ModelNeutralMutableEntry::UpdateTransactionVersion(int64 value) {
    367   ScopedKernelLock lock(dir());
    368   kernel_->put(TRANSACTION_VERSION, value);
    369   kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles));
    370 }
    371 
    372 ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans)
    373   : Entry(trans), base_write_transaction_(trans) {}
    374 
    375 MetahandleSet* ModelNeutralMutableEntry::GetDirtyIndexHelper() {
    376   return &dir()->kernel_->dirty_metahandles;
    377 }
    378 
    379 }  // namespace syncable
    380 
    381 }  // namespace syncer
    382