mutable_entry.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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 : ModelNeutralMutableEntry(trans) { 63 Init(trans, model_type, parent_id, name); 64 // We need to have a valid position ready before we can index the item. 65 if (model_type == BOOKMARKS) { 66 // Base the tag off of our cache-guid and local "c-" style ID. 67 std::string unique_tag = syncable::GenerateSyncableBookmarkHash( 68 trans->directory()->cache_guid(), GetId().GetServerId()); 69 kernel_->put(UNIQUE_BOOKMARK_TAG, unique_tag); 70 kernel_->put(UNIQUE_POSITION, UniquePosition::InitialPosition(unique_tag)); 71 } else { 72 DCHECK(!ShouldMaintainPosition()); 73 } 74 75 bool result = trans->directory()->InsertEntry(trans, kernel_); 76 DCHECK(result); 77} 78 79MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, 80 const Id& id) 81 : ModelNeutralMutableEntry(trans) { 82 Entry same_id(trans, GET_BY_ID, id); 83 kernel_ = NULL; 84 if (same_id.good()) { 85 return; // already have an item with this ID. 86 } 87 scoped_ptr<EntryKernel> kernel(new EntryKernel()); 88 89 kernel->put(ID, id); 90 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 91 kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles); 92 kernel->put(IS_DEL, true); 93 // We match the database defaults here 94 kernel->put(BASE_VERSION, CHANGES_VERSION); 95 if (!trans->directory()->InsertEntry(trans, kernel.get())) { 96 return; // Failed inserting. 97 } 98 trans->SaveOriginal(kernel.get()); 99 100 kernel_ = kernel.release(); 101} 102 103MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) 104 : ModelNeutralMutableEntry(trans, GET_BY_ID, id) { 105} 106 107MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, 108 int64 metahandle) 109 : ModelNeutralMutableEntry(trans, GET_BY_HANDLE, metahandle) { 110} 111 112MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, 113 const std::string& tag) 114 : ModelNeutralMutableEntry(trans, GET_BY_CLIENT_TAG, tag) { 115} 116 117MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, 118 const string& tag) 119 : ModelNeutralMutableEntry(trans, GET_BY_SERVER_TAG, tag) { 120} 121 122void MutableEntry::PutLocalExternalId(int64 value) { 123 DCHECK(kernel_); 124 write_transaction()->SaveOriginal(kernel_); 125 if (kernel_->ref(LOCAL_EXTERNAL_ID) != value) { 126 ScopedKernelLock lock(dir()); 127 kernel_->put(LOCAL_EXTERNAL_ID, value); 128 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 129 } 130} 131 132void MutableEntry::PutMtime(base::Time value) { 133 DCHECK(kernel_); 134 write_transaction()->SaveOriginal(kernel_); 135 if (kernel_->ref(MTIME) != value) { 136 kernel_->put(MTIME, value); 137 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 138 } 139} 140 141void MutableEntry::PutCtime(base::Time value) { 142 DCHECK(kernel_); 143 write_transaction()->SaveOriginal(kernel_); 144 if (kernel_->ref(CTIME) != value) { 145 kernel_->put(CTIME, value); 146 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 147 } 148} 149 150void MutableEntry::PutParentId(const Id& value) { 151 DCHECK(kernel_); 152 write_transaction()->SaveOriginal(kernel_); 153 if (kernel_->ref(PARENT_ID) != value) { 154 PutParentIdPropertyOnly(value); 155 if (!GetIsDel()) { 156 if (!PutPredecessor(Id())) { 157 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. 158 NOTREACHED(); 159 } 160 } 161 } 162} 163 164void MutableEntry::PutIsDir(bool value) { 165 DCHECK(kernel_); 166 write_transaction()->SaveOriginal(kernel_); 167 bool old_value = kernel_->ref(IS_DIR); 168 if (old_value != value) { 169 kernel_->put(IS_DIR, value); 170 kernel_->mark_dirty(GetDirtyIndexHelper()); 171 } 172} 173 174void MutableEntry::PutIsDel(bool value) { 175 DCHECK(kernel_); 176 write_transaction()->SaveOriginal(kernel_); 177 if (value == kernel_->ref(IS_DEL)) { 178 return; 179 } 180 if (value) { 181 // If the server never knew about this item and it's deleted then we don't 182 // need to keep it around. Unsetting IS_UNSYNCED will: 183 // - Ensure that the item is never committed to the server. 184 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other 185 // clients to override this entry. 186 // - Let us delete this entry permanently through 187 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. 188 // This will save memory and avoid crbug.com/125381. 189 if (!GetId().ServerKnows()) { 190 PutIsUnsynced(false); 191 } 192 } 193 194 { 195 ScopedKernelLock lock(dir()); 196 // Some indices don't include deleted items and must be updated 197 // upon a value change. 198 ScopedParentChildIndexUpdater updater(lock, kernel_, 199 &dir()->kernel_->parent_child_index); 200 201 kernel_->put(IS_DEL, value); 202 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 203 } 204} 205 206void MutableEntry::PutNonUniqueName(const std::string& value) { 207 DCHECK(kernel_); 208 write_transaction()->SaveOriginal(kernel_); 209 210 if (kernel_->ref(NON_UNIQUE_NAME) != value) { 211 kernel_->put(NON_UNIQUE_NAME, value); 212 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 213 } 214} 215 216void MutableEntry::PutSpecifics(const sync_pb::EntitySpecifics& value) { 217 DCHECK(kernel_); 218 CHECK(!value.password().has_client_only_encrypted_data()); 219 write_transaction()->SaveOriginal(kernel_); 220 // TODO(ncarter): This is unfortunately heavyweight. Can we do 221 // better? 222 if (kernel_->ref(SPECIFICS).SerializeAsString() != 223 value.SerializeAsString()) { 224 kernel_->put(SPECIFICS, value); 225 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 226 } 227} 228 229void MutableEntry::PutUniquePosition(const UniquePosition& value) { 230 DCHECK(kernel_); 231 write_transaction()->SaveOriginal(kernel_); 232 if(!kernel_->ref(UNIQUE_POSITION).Equals(value)) { 233 // We should never overwrite a valid position with an invalid one. 234 DCHECK(value.IsValid()); 235 ScopedKernelLock lock(dir()); 236 ScopedParentChildIndexUpdater updater( 237 lock, kernel_, &dir()->kernel_->parent_child_index); 238 kernel_->put(UNIQUE_POSITION, value); 239 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 240 } 241} 242 243bool MutableEntry::PutPredecessor(const Id& predecessor_id) { 244 MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id); 245 if (!predecessor.good()) 246 return false; 247 dir()->PutPredecessor(kernel_, predecessor.kernel_); 248 return true; 249} 250 251// This function sets only the flags needed to get this entry to sync. 252bool MarkForSyncing(MutableEntry* e) { 253 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); 254 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; 255 if (!(e->PutIsUnsynced(true))) 256 return false; 257 e->PutSyncing(false); 258 return true; 259} 260 261} // namespace syncable 262} // namespace syncer 263