Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: sync/syncable/directory_backing_store.cc

Issue 11441026: [Sync] Add support for loading, updating and querying delete journals in (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698