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