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 22 matching lines...) Expand all Loading... | |
33 | 33 |
34 namespace syncer { | 34 namespace syncer { |
35 namespace syncable { | 35 namespace syncable { |
36 | 36 |
37 // This just has to be big enough to hold an UPDATE or INSERT statement that | 37 // This just has to be big enough to hold an UPDATE or INSERT statement that |
38 // modifies all the columns in the entry table. | 38 // modifies all the columns in the entry table. |
39 static const string::size_type kUpdateStatementBufferSize = 2048; | 39 static const string::size_type kUpdateStatementBufferSize = 2048; |
40 | 40 |
41 // Increment this version whenever updating DB tables. | 41 // Increment this version whenever updating DB tables. |
42 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. | 42 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. |
43 const int32 kCurrentDBVersion = 83; | 43 const int32 kCurrentDBVersion = 84; |
44 | 44 |
45 // Iterate over the fields of |entry| and bind each to |statement| for | 45 // Iterate over the fields of |entry| and bind each to |statement| for |
46 // updating. Returns the number of args bound. | 46 // updating. Returns the number of args bound. |
47 void BindFields(const EntryKernel& entry, | 47 void BindFields(const EntryKernel& entry, |
48 sql::Statement* statement) { | 48 sql::Statement* statement) { |
49 int index = 0; | 49 int index = 0; |
50 int i = 0; | 50 int i = 0; |
51 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { | 51 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { |
52 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i))); | 52 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i))); |
53 } | 53 } |
(...skipping 111 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(bool meta_delete, |
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; |
180 SQL_FROM_HERE, "DELETE FROM metas WHERE metahandle = ?")); | 181 // Call GetCachedStatement() separately to get different statements for |
182 // different tables. | |
183 if (meta_delete) { | |
184 statement.Assign(db_->GetCachedStatement( | |
185 SQL_FROM_HERE, "DELETE FROM metas WHERE metahandle = ?")); | |
186 } else { | |
187 statement.Assign(db_->GetCachedStatement( | |
188 SQL_FROM_HERE, "DELETE FROM deleted_metas WHERE metahandle = ?")); | |
189 } | |
181 | 190 |
182 for (MetahandleSet::const_iterator i = handles.begin(); i != handles.end(); | 191 for (MetahandleSet::const_iterator i = handles.begin(); i != handles.end(); |
183 ++i) { | 192 ++i) { |
184 statement.BindInt64(0, *i); | 193 statement.BindInt64(0, *i); |
185 if (!statement.Run()) | 194 if (!statement.Run()) |
186 return false; | 195 return false; |
187 statement.Reset(true); | 196 statement.Reset(true); |
188 } | 197 } |
189 return true; | 198 return true; |
190 } | 199 } |
191 | 200 |
192 bool DirectoryBackingStore::SaveChanges( | 201 bool DirectoryBackingStore::SaveChanges( |
193 const Directory::SaveChangesSnapshot& snapshot) { | 202 const Directory::SaveChangesSnapshot& snapshot) { |
194 DCHECK(CalledOnValidThread()); | 203 DCHECK(CalledOnValidThread()); |
195 DCHECK(db_->is_open()); | 204 DCHECK(db_->is_open()); |
196 | 205 |
197 // Back out early if there is nothing to write. | 206 // Back out early if there is nothing to write. |
198 bool save_info = | 207 bool save_info = |
199 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status); | 208 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status); |
200 if (snapshot.dirty_metas.size() < 1 && !save_info) | 209 if (snapshot.dirty_metas.empty() && snapshot.metahandles_to_purge.empty() && |
210 snapshot.delete_journals.empty() && | |
211 snapshot.delete_journals_to_purge.empty() && !save_info) | |
tim (not reviewing)
2012/12/13 23:41:30
Braces around if body
haitaol1
2012/12/14 19:22:38
Done.
| |
201 return true; | 212 return true; |
202 | 213 |
203 sql::Transaction transaction(db_.get()); | 214 sql::Transaction transaction(db_.get()); |
204 if (!transaction.Begin()) | 215 if (!transaction.Begin()) |
205 return false; | 216 return false; |
206 | 217 |
218 PrepareSaveEntryStatement("metas", &save_meta_statment_); | |
207 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); | 219 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); |
208 i != snapshot.dirty_metas.end(); ++i) { | 220 i != snapshot.dirty_metas.end(); ++i) { |
209 DCHECK(i->is_dirty()); | 221 DCHECK((*i)->is_dirty()); |
210 if (!SaveEntryToDB(*i)) | 222 if (!SaveEntryToDB(&save_meta_statment_, **i)) |
211 return false; | 223 return false; |
212 } | 224 } |
213 | 225 |
214 if (!DeleteEntries(snapshot.metahandles_to_purge)) | 226 if (!DeleteEntries(true, snapshot.metahandles_to_purge)) |
227 return false; | |
228 | |
229 PrepareSaveEntryStatement("deleted_metas", &save_delete_journal_statment_); | |
230 for (EntryKernelSet::const_iterator i = snapshot.delete_journals.begin(); | |
231 i != snapshot.delete_journals.end(); ++i) { | |
232 if (!SaveEntryToDB(&save_delete_journal_statment_, **i)) | |
233 return false; | |
234 } | |
235 | |
236 if (!DeleteEntries(false, snapshot.delete_journals_to_purge)) | |
215 return false; | 237 return false; |
216 | 238 |
217 if (save_info) { | 239 if (save_info) { |
218 const Directory::PersistedKernelInfo& info = snapshot.kernel_info; | 240 const Directory::PersistedKernelInfo& info = snapshot.kernel_info; |
219 sql::Statement s1(db_->GetCachedStatement( | 241 sql::Statement s1(db_->GetCachedStatement( |
220 SQL_FROM_HERE, | 242 SQL_FROM_HERE, |
221 "UPDATE share_info " | 243 "UPDATE share_info " |
222 "SET store_birthday = ?, " | 244 "SET store_birthday = ?, " |
223 "next_id = ?, " | 245 "next_id = ?, " |
224 "notification_state = ?, " | 246 "notification_state = ?, " |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
357 if (MigrateVersion81To82()) | 379 if (MigrateVersion81To82()) |
358 version_on_disk = 82; | 380 version_on_disk = 82; |
359 } | 381 } |
360 | 382 |
361 // Version 83 migration added transaction_version column per sync entry. | 383 // Version 83 migration added transaction_version column per sync entry. |
362 if (version_on_disk == 82) { | 384 if (version_on_disk == 82) { |
363 if (MigrateVersion82To83()) | 385 if (MigrateVersion82To83()) |
364 version_on_disk = 83; | 386 version_on_disk = 83; |
365 } | 387 } |
366 | 388 |
389 // Version 84 migration added deleted_metas table. | |
390 if (version_on_disk == 83) { | |
391 if (MigrateVersion83To84()) | |
392 version_on_disk = 84; | |
393 } | |
394 | |
367 // If one of the migrations requested it, drop columns that aren't current. | 395 // If one of the migrations requested it, drop columns that aren't current. |
368 // It's only safe to do this after migrating all the way to the current | 396 // It's only safe to do this after migrating all the way to the current |
369 // version. | 397 // version. |
370 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { | 398 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { |
371 if (!RefreshColumns()) | 399 if (!RefreshColumns()) |
372 version_on_disk = 0; | 400 version_on_disk = 0; |
373 } | 401 } |
374 | 402 |
375 // A final, alternative catch-all migration to simply re-sync everything. | 403 // A final, alternative catch-all migration to simply re-sync everything. |
376 // | 404 // |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
453 | 481 |
454 SafeDropTable("share_info"); | 482 SafeDropTable("share_info"); |
455 if (!db_->Execute("ALTER TABLE temp_share_info RENAME TO share_info")) | 483 if (!db_->Execute("ALTER TABLE temp_share_info RENAME TO share_info")) |
456 return false; | 484 return false; |
457 | 485 |
458 needs_column_refresh_ = false; | 486 needs_column_refresh_ = false; |
459 return true; | 487 return true; |
460 } | 488 } |
461 | 489 |
462 bool DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) { | 490 bool DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) { |
463 string select; | 491 return LoadEntriesInternal("metas", entry_bucket); |
464 select.reserve(kUpdateStatementBufferSize); | 492 } |
465 select.append("SELECT "); | |
466 AppendColumnList(&select); | |
467 select.append(" FROM metas "); | |
468 | 493 |
469 sql::Statement s(db_->GetUniqueStatement(select.c_str())); | 494 bool DirectoryBackingStore::LoadDeleteJournals( |
470 | 495 IdsIndex* delete_journals) { |
471 while (s.Step()) { | 496 return LoadEntriesInternal("deleted_metas", delete_journals); |
472 scoped_ptr<EntryKernel> kernel = UnpackEntry(&s); | |
473 // A null kernel is evidence of external data corruption. | |
474 if (!kernel.get()) | |
475 return false; | |
476 entry_bucket->insert(kernel.release()); | |
477 } | |
478 return s.Succeeded(); | |
479 } | 497 } |
480 | 498 |
481 bool DirectoryBackingStore::LoadInfo(Directory::KernelLoadInfo* info) { | 499 bool DirectoryBackingStore::LoadInfo(Directory::KernelLoadInfo* info) { |
482 { | 500 { |
483 sql::Statement s( | 501 sql::Statement s( |
484 db_->GetUniqueStatement( | 502 db_->GetUniqueStatement( |
485 "SELECT store_birthday, next_id, cache_guid, notification_state, " | 503 "SELECT store_birthday, next_id, cache_guid, notification_state, " |
486 "bag_of_chips " | 504 "bag_of_chips " |
487 "FROM share_info")); | 505 "FROM share_info")); |
488 if (!s.Step()) | 506 if (!s.Step()) |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
528 | 546 |
529 info->max_metahandle = s.ColumnInt64(0); | 547 info->max_metahandle = s.ColumnInt64(0); |
530 | 548 |
531 // Verify only one row was returned. | 549 // Verify only one row was returned. |
532 DCHECK(!s.Step()); | 550 DCHECK(!s.Step()); |
533 DCHECK(s.Succeeded()); | 551 DCHECK(s.Succeeded()); |
534 } | 552 } |
535 return true; | 553 return true; |
536 } | 554 } |
537 | 555 |
538 bool DirectoryBackingStore::SaveEntryToDB(const EntryKernel& entry) { | 556 /* static */ |
539 // This statement is constructed at runtime, so we can't use | 557 bool DirectoryBackingStore::SaveEntryToDB(sql::Statement* save_statement, |
540 // GetCachedStatement() to let the Connection cache it. We will construct | 558 const EntryKernel& entry) { |
541 // and cache it ourselves the first time this function is called. | 559 save_statement->Reset(true); |
542 if (!save_entry_statement_.is_valid()) { | 560 BindFields(entry, save_statement); |
543 string query; | 561 return save_statement->Run(); |
544 query.reserve(kUpdateStatementBufferSize); | |
545 query.append("INSERT OR REPLACE INTO metas "); | |
546 string values; | |
547 values.reserve(kUpdateStatementBufferSize); | |
548 values.append("VALUES "); | |
549 const char* separator = "( "; | |
550 int i = 0; | |
551 for (i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) { | |
552 query.append(separator); | |
553 values.append(separator); | |
554 separator = ", "; | |
555 query.append(ColumnName(i)); | |
556 values.append("?"); | |
557 } | |
558 query.append(" ) "); | |
559 values.append(" )"); | |
560 query.append(values); | |
561 | |
562 save_entry_statement_.Assign( | |
563 db_->GetUniqueStatement(query.c_str())); | |
564 } else { | |
565 save_entry_statement_.Reset(true); | |
566 } | |
567 | |
568 BindFields(entry, &save_entry_statement_); | |
569 return save_entry_statement_.Run(); | |
570 } | 562 } |
571 | 563 |
572 bool DirectoryBackingStore::DropDeletedEntries() { | 564 bool DirectoryBackingStore::DropDeletedEntries() { |
573 if (!db_->Execute("DELETE FROM metas " | 565 if (!db_->Execute("DELETE FROM metas " |
574 "WHERE is_del > 0 " | 566 "WHERE is_del > 0 " |
575 "AND is_unsynced < 1 " | 567 "AND is_unsynced < 1 " |
576 "AND is_unapplied_update < 1")) { | 568 "AND is_unapplied_update < 1")) { |
577 return false; | 569 return false; |
578 } | 570 } |
579 if (!db_->Execute("DELETE FROM metas " | 571 if (!db_->Execute("DELETE FROM metas " |
(...skipping 504 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1084 "ALTER TABLE metas ADD COLUMN transaction_version BIGINT default 0")) | 1076 "ALTER TABLE metas ADD COLUMN transaction_version BIGINT default 0")) |
1085 return false; | 1077 return false; |
1086 sql::Statement update(db_->GetUniqueStatement( | 1078 sql::Statement update(db_->GetUniqueStatement( |
1087 "UPDATE metas SET transaction_version = 0")); | 1079 "UPDATE metas SET transaction_version = 0")); |
1088 if (!update.Run()) | 1080 if (!update.Run()) |
1089 return false; | 1081 return false; |
1090 SetVersion(83); | 1082 SetVersion(83); |
1091 return true; | 1083 return true; |
1092 } | 1084 } |
1093 | 1085 |
1086 bool DirectoryBackingStore::MigrateVersion83To84() { | |
1087 // Version 84 added deleted_metas table to store deleted metas until we know | |
1088 // for sure that the deletions are persisted in native models. | |
1089 string query = "CREATE TABLE deleted_metas "; | |
1090 query.append(ComposeCreateTableColumnSpecs()); | |
1091 if (!db_->Execute(query.c_str())) | |
1092 return false; | |
1093 SetVersion(84); | |
1094 return true; | |
1095 } | |
1096 | |
1094 bool DirectoryBackingStore::CreateTables() { | 1097 bool DirectoryBackingStore::CreateTables() { |
1095 DVLOG(1) << "First run, creating tables"; | 1098 DVLOG(1) << "First run, creating tables"; |
1096 // Create two little tables share_version and share_info | 1099 // Create two little tables share_version and share_info |
1097 if (!db_->Execute( | 1100 if (!db_->Execute( |
1098 "CREATE TABLE share_version (" | 1101 "CREATE TABLE share_version (" |
1099 "id VARCHAR(128) primary key, data INT)")) { | 1102 "id VARCHAR(128) primary key, data INT)")) { |
1100 return false; | 1103 return false; |
1101 } | 1104 } |
1102 | 1105 |
1103 { | 1106 { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1162 s.BindBlob(2, ord.data(), ord.length()); | 1165 s.BindBlob(2, ord.data(), ord.length()); |
1163 | 1166 |
1164 if (!s.Run()) | 1167 if (!s.Run()) |
1165 return false; | 1168 return false; |
1166 } | 1169 } |
1167 | 1170 |
1168 return true; | 1171 return true; |
1169 } | 1172 } |
1170 | 1173 |
1171 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) { | 1174 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) { |
1172 const char* name = is_temporary ? "temp_metas" : "metas"; | |
1173 string query = "CREATE TABLE "; | 1175 string query = "CREATE TABLE "; |
1174 query.append(name); | 1176 query.append(is_temporary ? "temp_metas" : "metas"); |
1177 query.append(ComposeCreateTableColumnSpecs()); | |
1178 if (!db_->Execute(query.c_str())) | |
1179 return false; | |
1180 | |
1181 // Create a deleted_metas table to save copies of deleted metas until the | |
1182 // deletions are persisted. For simplicity, don't try to migrate existing | |
1183 // data because it's rarely used. | |
1184 SafeDropTable("deleted_metas"); | |
1185 query = "CREATE TABLE deleted_metas "; | |
1175 query.append(ComposeCreateTableColumnSpecs()); | 1186 query.append(ComposeCreateTableColumnSpecs()); |
1176 return db_->Execute(query.c_str()); | 1187 return db_->Execute(query.c_str()); |
1177 } | 1188 } |
1178 | 1189 |
1179 bool DirectoryBackingStore::CreateV71ModelsTable() { | 1190 bool DirectoryBackingStore::CreateV71ModelsTable() { |
1180 // This is an old schema for the Models table, used from versions 71 to 74. | 1191 // This is an old schema for the Models table, used from versions 71 to 74. |
1181 return db_->Execute( | 1192 return db_->Execute( |
1182 "CREATE TABLE models (" | 1193 "CREATE TABLE models (" |
1183 "model_id BLOB primary key, " | 1194 "model_id BLOB primary key, " |
1184 "last_download_timestamp INT, " | 1195 "last_download_timestamp INT, " |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1264 it != index.end(); ++it) { | 1275 it != index.end(); ++it) { |
1265 EntryKernel* entry = *it; | 1276 EntryKernel* entry = *it; |
1266 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); | 1277 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); |
1267 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); | 1278 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); |
1268 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); | 1279 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); |
1269 is_ok = is_ok && prev_exists && parent_exists && next_exists; | 1280 is_ok = is_ok && prev_exists && parent_exists && next_exists; |
1270 } | 1281 } |
1271 return is_ok; | 1282 return is_ok; |
1272 } | 1283 } |
1273 | 1284 |
1285 template<class T> | |
1286 bool DirectoryBackingStore::LoadEntriesInternal(const std::string& table, | |
1287 T* bucket) { | |
1288 string select; | |
1289 select.reserve(kUpdateStatementBufferSize); | |
1290 select.append("SELECT "); | |
1291 AppendColumnList(&select); | |
1292 select.append(" FROM " + table); | |
1293 | |
1294 sql::Statement s(db_->GetUniqueStatement(select.c_str())); | |
1295 | |
1296 while (s.Step()) { | |
1297 scoped_ptr<EntryKernel> kernel = UnpackEntry(&s); | |
1298 // A null kernel is evidence of external data corruption. | |
1299 if (!kernel.get()) | |
1300 return false; | |
1301 bucket->insert(kernel.release()); | |
1302 } | |
1303 return s.Succeeded(); | |
1304 } | |
1305 | |
1306 void DirectoryBackingStore::PrepareSaveEntryStatement( | |
1307 const std::string& table, sql::Statement* save_statement) { | |
1308 if (save_statement->is_valid()) | |
1309 return; | |
1310 | |
1311 string query; | |
1312 query.reserve(kUpdateStatementBufferSize); | |
1313 query.append("INSERT OR REPLACE INTO " + table); | |
1314 string values; | |
1315 values.reserve(kUpdateStatementBufferSize); | |
1316 values.append(" VALUES "); | |
1317 const char* separator = "( "; | |
1318 int i = 0; | |
1319 for (i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) { | |
1320 query.append(separator); | |
1321 values.append(separator); | |
1322 separator = ", "; | |
1323 query.append(ColumnName(i)); | |
1324 values.append("?"); | |
1325 } | |
1326 query.append(" ) "); | |
1327 values.append(" )"); | |
1328 query.append(values); | |
1329 save_statement->Assign(db_->GetUniqueStatement( | |
1330 base::StringPrintf(query.c_str(), "metas").c_str())); | |
1331 } | |
1332 | |
1274 } // namespace syncable | 1333 } // namespace syncable |
1275 } // namespace syncer | 1334 } // namespace syncer |
OLD | NEW |