| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sync/syncable/directory_backing_store.h" | 5 #include "sync/syncable/directory_backing_store.h" |
| 6 | 6 |
| 7 #include "build/build_config.h" | 7 #include "build/build_config.h" |
| 8 | 8 |
| 9 #include <limits> | 9 #include <limits> |
| 10 | 10 |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 DirectoryBackingStore::DirectoryBackingStore(const string& dir_name, | 164 DirectoryBackingStore::DirectoryBackingStore(const string& dir_name, |
| 165 sql::Connection* db) | 165 sql::Connection* db) |
| 166 : db_(db), | 166 : db_(db), |
| 167 dir_name_(dir_name), | 167 dir_name_(dir_name), |
| 168 needs_column_refresh_(false) { | 168 needs_column_refresh_(false) { |
| 169 } | 169 } |
| 170 | 170 |
| 171 DirectoryBackingStore::~DirectoryBackingStore() { | 171 DirectoryBackingStore::~DirectoryBackingStore() { |
| 172 } | 172 } |
| 173 | 173 |
| 174 bool DirectoryBackingStore::DeleteEntries(const MetahandleSet& handles) { | 174 bool DirectoryBackingStore::DeleteEntries(EntryTable from, |
| 175 const MetahandleSet& handles) { |
| 175 if (handles.empty()) | 176 if (handles.empty()) |
| 176 return true; | 177 return true; |
| 177 | 178 |
| 178 sql::Statement statement(db_->GetCachedStatement( | 179 sql::Statement statement; |
| 180 // Call GetCachedStatement() separately to get different statements for |
| 181 // different tables. |
| 182 switch (from) { |
| 183 case METAS_TABLE: |
| 184 statement.Assign(db_->GetCachedStatement( |
| 179 SQL_FROM_HERE, "DELETE FROM metas WHERE metahandle = ?")); | 185 SQL_FROM_HERE, "DELETE FROM metas WHERE metahandle = ?")); |
| 186 break; |
| 187 case DELETE_JOURNAL_TABLE: |
| 188 statement.Assign(db_->GetCachedStatement( |
| 189 SQL_FROM_HERE, "DELETE FROM deleted_metas WHERE metahandle = ?")); |
| 190 break; |
| 191 } |
| 180 | 192 |
| 181 for (MetahandleSet::const_iterator i = handles.begin(); i != handles.end(); | 193 for (MetahandleSet::const_iterator i = handles.begin(); i != handles.end(); |
| 182 ++i) { | 194 ++i) { |
| 183 statement.BindInt64(0, *i); | 195 statement.BindInt64(0, *i); |
| 184 if (!statement.Run()) | 196 if (!statement.Run()) |
| 185 return false; | 197 return false; |
| 186 statement.Reset(true); | 198 statement.Reset(true); |
| 187 } | 199 } |
| 188 return true; | 200 return true; |
| 189 } | 201 } |
| 190 | 202 |
| 191 bool DirectoryBackingStore::SaveChanges( | 203 bool DirectoryBackingStore::SaveChanges( |
| 192 const Directory::SaveChangesSnapshot& snapshot) { | 204 const Directory::SaveChangesSnapshot& snapshot) { |
| 193 DCHECK(CalledOnValidThread()); | 205 DCHECK(CalledOnValidThread()); |
| 194 DCHECK(db_->is_open()); | 206 DCHECK(db_->is_open()); |
| 195 | 207 |
| 196 // Back out early if there is nothing to write. | 208 // Back out early if there is nothing to write. |
| 197 bool save_info = | 209 bool save_info = |
| 198 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status); | 210 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status); |
| 199 if (snapshot.dirty_metas.empty() | 211 if (snapshot.dirty_metas.empty() && snapshot.metahandles_to_purge.empty() && |
| 200 && snapshot.metahandles_to_purge.empty() | 212 snapshot.delete_journals.empty() && |
| 201 && !save_info) { | 213 snapshot.delete_journals_to_purge.empty() && !save_info) { |
| 202 return true; | 214 return true; |
| 203 } | 215 } |
| 204 | 216 |
| 205 sql::Transaction transaction(db_.get()); | 217 sql::Transaction transaction(db_.get()); |
| 206 if (!transaction.Begin()) | 218 if (!transaction.Begin()) |
| 207 return false; | 219 return false; |
| 208 | 220 |
| 221 PrepareSaveEntryStatement(METAS_TABLE, &save_meta_statment_); |
| 209 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); | 222 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); |
| 210 i != snapshot.dirty_metas.end(); ++i) { | 223 i != snapshot.dirty_metas.end(); ++i) { |
| 211 DCHECK(i->is_dirty()); | 224 DCHECK((*i)->is_dirty()); |
| 212 if (!SaveEntryToDB(*i)) | 225 if (!SaveEntryToDB(&save_meta_statment_, **i)) |
| 213 return false; | 226 return false; |
| 214 } | 227 } |
| 215 | 228 |
| 216 if (!DeleteEntries(snapshot.metahandles_to_purge)) | 229 if (!DeleteEntries(METAS_TABLE, snapshot.metahandles_to_purge)) |
| 230 return false; |
| 231 |
| 232 PrepareSaveEntryStatement(DELETE_JOURNAL_TABLE, |
| 233 &save_delete_journal_statment_); |
| 234 for (EntryKernelSet::const_iterator i = snapshot.delete_journals.begin(); |
| 235 i != snapshot.delete_journals.end(); ++i) { |
| 236 if (!SaveEntryToDB(&save_delete_journal_statment_, **i)) |
| 237 return false; |
| 238 } |
| 239 |
| 240 if (!DeleteEntries(DELETE_JOURNAL_TABLE, snapshot.delete_journals_to_purge)) |
| 217 return false; | 241 return false; |
| 218 | 242 |
| 219 if (save_info) { | 243 if (save_info) { |
| 220 const Directory::PersistedKernelInfo& info = snapshot.kernel_info; | 244 const Directory::PersistedKernelInfo& info = snapshot.kernel_info; |
| 221 sql::Statement s1(db_->GetCachedStatement( | 245 sql::Statement s1(db_->GetCachedStatement( |
| 222 SQL_FROM_HERE, | 246 SQL_FROM_HERE, |
| 223 "UPDATE share_info " | 247 "UPDATE share_info " |
| 224 "SET store_birthday = ?, " | 248 "SET store_birthday = ?, " |
| 225 "next_id = ?, " | 249 "next_id = ?, " |
| 226 "notification_state = ?, " | 250 "notification_state = ?, " |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 | 482 |
| 459 SafeDropTable("share_info"); | 483 SafeDropTable("share_info"); |
| 460 if (!db_->Execute("ALTER TABLE temp_share_info RENAME TO share_info")) | 484 if (!db_->Execute("ALTER TABLE temp_share_info RENAME TO share_info")) |
| 461 return false; | 485 return false; |
| 462 | 486 |
| 463 needs_column_refresh_ = false; | 487 needs_column_refresh_ = false; |
| 464 return true; | 488 return true; |
| 465 } | 489 } |
| 466 | 490 |
| 467 bool DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) { | 491 bool DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) { |
| 468 string select; | 492 return LoadEntriesInternal("metas", entry_bucket); |
| 469 select.reserve(kUpdateStatementBufferSize); | 493 } |
| 470 select.append("SELECT "); | |
| 471 AppendColumnList(&select); | |
| 472 select.append(" FROM metas "); | |
| 473 | 494 |
| 474 sql::Statement s(db_->GetUniqueStatement(select.c_str())); | 495 bool DirectoryBackingStore::LoadDeleteJournals( |
| 475 | 496 JournalIndex* delete_journals) { |
| 476 while (s.Step()) { | 497 return LoadEntriesInternal("deleted_metas", delete_journals); |
| 477 scoped_ptr<EntryKernel> kernel = UnpackEntry(&s); | |
| 478 // A null kernel is evidence of external data corruption. | |
| 479 if (!kernel.get()) | |
| 480 return false; | |
| 481 entry_bucket->insert(kernel.release()); | |
| 482 } | |
| 483 return s.Succeeded(); | |
| 484 } | 498 } |
| 485 | 499 |
| 486 bool DirectoryBackingStore::LoadInfo(Directory::KernelLoadInfo* info) { | 500 bool DirectoryBackingStore::LoadInfo(Directory::KernelLoadInfo* info) { |
| 487 { | 501 { |
| 488 sql::Statement s( | 502 sql::Statement s( |
| 489 db_->GetUniqueStatement( | 503 db_->GetUniqueStatement( |
| 490 "SELECT store_birthday, next_id, cache_guid, notification_state, " | 504 "SELECT store_birthday, next_id, cache_guid, notification_state, " |
| 491 "bag_of_chips " | 505 "bag_of_chips " |
| 492 "FROM share_info")); | 506 "FROM share_info")); |
| 493 if (!s.Step()) | 507 if (!s.Step()) |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 531 | 545 |
| 532 info->max_metahandle = s.ColumnInt64(0); | 546 info->max_metahandle = s.ColumnInt64(0); |
| 533 | 547 |
| 534 // Verify only one row was returned. | 548 // Verify only one row was returned. |
| 535 DCHECK(!s.Step()); | 549 DCHECK(!s.Step()); |
| 536 DCHECK(s.Succeeded()); | 550 DCHECK(s.Succeeded()); |
| 537 } | 551 } |
| 538 return true; | 552 return true; |
| 539 } | 553 } |
| 540 | 554 |
| 541 bool DirectoryBackingStore::SaveEntryToDB(const EntryKernel& entry) { | 555 /* static */ |
| 542 // This statement is constructed at runtime, so we can't use | 556 bool DirectoryBackingStore::SaveEntryToDB(sql::Statement* save_statement, |
| 543 // GetCachedStatement() to let the Connection cache it. We will construct | 557 const EntryKernel& entry) { |
| 544 // and cache it ourselves the first time this function is called. | 558 save_statement->Reset(true); |
| 545 if (!save_entry_statement_.is_valid()) { | 559 BindFields(entry, save_statement); |
| 546 string query; | 560 return save_statement->Run(); |
| 547 query.reserve(kUpdateStatementBufferSize); | |
| 548 query.append("INSERT OR REPLACE INTO metas "); | |
| 549 string values; | |
| 550 values.reserve(kUpdateStatementBufferSize); | |
| 551 values.append("VALUES "); | |
| 552 const char* separator = "( "; | |
| 553 int i = 0; | |
| 554 for (i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) { | |
| 555 query.append(separator); | |
| 556 values.append(separator); | |
| 557 separator = ", "; | |
| 558 query.append(ColumnName(i)); | |
| 559 values.append("?"); | |
| 560 } | |
| 561 query.append(" ) "); | |
| 562 values.append(" )"); | |
| 563 query.append(values); | |
| 564 | |
| 565 save_entry_statement_.Assign( | |
| 566 db_->GetUniqueStatement(query.c_str())); | |
| 567 } else { | |
| 568 save_entry_statement_.Reset(true); | |
| 569 } | |
| 570 | |
| 571 BindFields(entry, &save_entry_statement_); | |
| 572 return save_entry_statement_.Run(); | |
| 573 } | 561 } |
| 574 | 562 |
| 575 bool DirectoryBackingStore::DropDeletedEntries() { | 563 bool DirectoryBackingStore::DropDeletedEntries() { |
| 576 if (!db_->Execute("DELETE FROM metas " | 564 if (!db_->Execute("DELETE FROM metas " |
| 577 "WHERE is_del > 0 " | 565 "WHERE is_del > 0 " |
| 578 "AND is_unsynced < 1 " | 566 "AND is_unsynced < 1 " |
| 579 "AND is_unapplied_update < 1")) { | 567 "AND is_unapplied_update < 1")) { |
| 580 return false; | 568 return false; |
| 581 } | 569 } |
| 582 if (!db_->Execute("DELETE FROM metas " | 570 if (!db_->Execute("DELETE FROM metas " |
| (...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1086 return true; | 1074 return true; |
| 1087 } | 1075 } |
| 1088 | 1076 |
| 1089 bool DirectoryBackingStore::MigrateVersion83To84() { | 1077 bool DirectoryBackingStore::MigrateVersion83To84() { |
| 1090 // Version 84 added deleted_metas table to store deleted metas until we know | 1078 // Version 84 added deleted_metas table to store deleted metas until we know |
| 1091 // for sure that the deletions are persisted in native models. | 1079 // for sure that the deletions are persisted in native models. |
| 1092 string query = "CREATE TABLE deleted_metas "; | 1080 string query = "CREATE TABLE deleted_metas "; |
| 1093 query.append(ComposeCreateTableColumnSpecs()); | 1081 query.append(ComposeCreateTableColumnSpecs()); |
| 1094 if (!db_->Execute(query.c_str())) | 1082 if (!db_->Execute(query.c_str())) |
| 1095 return false; | 1083 return false; |
| 1096 | |
| 1097 SetVersion(84); | 1084 SetVersion(84); |
| 1098 return true; | 1085 return true; |
| 1099 } | 1086 } |
| 1100 | 1087 |
| 1101 bool DirectoryBackingStore::MigrateVersion84To85() { | 1088 bool DirectoryBackingStore::MigrateVersion84To85() { |
| 1102 // Version 84 removes the initial_sync_ended flag. | 1089 // Version 84 removes the initial_sync_ended flag. |
| 1103 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) | 1090 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) |
| 1104 return false; | 1091 return false; |
| 1105 if (!CreateModelsTable()) | 1092 if (!CreateModelsTable()) |
| 1106 return false; | 1093 return false; |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1307 it != index.end(); ++it) { | 1294 it != index.end(); ++it) { |
| 1308 EntryKernel* entry = *it; | 1295 EntryKernel* entry = *it; |
| 1309 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); | 1296 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); |
| 1310 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); | 1297 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); |
| 1311 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); | 1298 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); |
| 1312 is_ok = is_ok && prev_exists && parent_exists && next_exists; | 1299 is_ok = is_ok && prev_exists && parent_exists && next_exists; |
| 1313 } | 1300 } |
| 1314 return is_ok; | 1301 return is_ok; |
| 1315 } | 1302 } |
| 1316 | 1303 |
| 1304 template<class T> |
| 1305 bool DirectoryBackingStore::LoadEntriesInternal(const std::string& table, |
| 1306 T* bucket) { |
| 1307 string select; |
| 1308 select.reserve(kUpdateStatementBufferSize); |
| 1309 select.append("SELECT "); |
| 1310 AppendColumnList(&select); |
| 1311 select.append(" FROM " + table); |
| 1312 |
| 1313 sql::Statement s(db_->GetUniqueStatement(select.c_str())); |
| 1314 |
| 1315 while (s.Step()) { |
| 1316 scoped_ptr<EntryKernel> kernel = UnpackEntry(&s); |
| 1317 // A null kernel is evidence of external data corruption. |
| 1318 if (!kernel.get()) |
| 1319 return false; |
| 1320 bucket->insert(kernel.release()); |
| 1321 } |
| 1322 return s.Succeeded(); |
| 1323 } |
| 1324 |
| 1325 void DirectoryBackingStore::PrepareSaveEntryStatement( |
| 1326 EntryTable table, sql::Statement* save_statement) { |
| 1327 if (save_statement->is_valid()) |
| 1328 return; |
| 1329 |
| 1330 string query; |
| 1331 query.reserve(kUpdateStatementBufferSize); |
| 1332 switch (table) { |
| 1333 case METAS_TABLE: |
| 1334 query.append("INSERT OR REPLACE INTO metas "); |
| 1335 break; |
| 1336 case DELETE_JOURNAL_TABLE: |
| 1337 query.append("INSERT OR REPLACE INTO deleted_metas "); |
| 1338 break; |
| 1339 } |
| 1340 |
| 1341 string values; |
| 1342 values.reserve(kUpdateStatementBufferSize); |
| 1343 values.append(" VALUES "); |
| 1344 const char* separator = "( "; |
| 1345 int i = 0; |
| 1346 for (i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) { |
| 1347 query.append(separator); |
| 1348 values.append(separator); |
| 1349 separator = ", "; |
| 1350 query.append(ColumnName(i)); |
| 1351 values.append("?"); |
| 1352 } |
| 1353 query.append(" ) "); |
| 1354 values.append(" )"); |
| 1355 query.append(values); |
| 1356 save_statement->Assign(db_->GetUniqueStatement( |
| 1357 base::StringPrintf(query.c_str(), "metas").c_str())); |
| 1358 } |
| 1359 |
| 1317 } // namespace syncable | 1360 } // namespace syncable |
| 1318 } // namespace syncer | 1361 } // namespace syncer |
| OLD | NEW |