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 = 84; | 43 const int32 kCurrentDBVersion = 85; |
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 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
190 } | 190 } |
191 | 191 |
192 bool DirectoryBackingStore::SaveChanges( | 192 bool DirectoryBackingStore::SaveChanges( |
193 const Directory::SaveChangesSnapshot& snapshot) { | 193 const Directory::SaveChangesSnapshot& snapshot) { |
194 DCHECK(CalledOnValidThread()); | 194 DCHECK(CalledOnValidThread()); |
195 DCHECK(db_->is_open()); | 195 DCHECK(db_->is_open()); |
196 | 196 |
197 // Back out early if there is nothing to write. | 197 // Back out early if there is nothing to write. |
198 bool save_info = | 198 bool save_info = |
199 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status); | 199 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status); |
200 if (snapshot.dirty_metas.size() < 1 && !save_info) | 200 if (snapshot.dirty_metas.empty() |
| 201 && snapshot.metahandles_to_purge.empty() |
| 202 && !save_info) { |
201 return true; | 203 return true; |
| 204 } |
202 | 205 |
203 sql::Transaction transaction(db_.get()); | 206 sql::Transaction transaction(db_.get()); |
204 if (!transaction.Begin()) | 207 if (!transaction.Begin()) |
205 return false; | 208 return false; |
206 | 209 |
207 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); | 210 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); |
208 i != snapshot.dirty_metas.end(); ++i) { | 211 i != snapshot.dirty_metas.end(); ++i) { |
209 DCHECK(i->is_dirty()); | 212 DCHECK(i->is_dirty()); |
210 if (!SaveEntryToDB(*i)) | 213 if (!SaveEntryToDB(*i)) |
211 return false; | 214 return false; |
(...skipping 17 matching lines...) Expand all Loading... |
229 info.notification_state.size()); | 232 info.notification_state.size()); |
230 s1.BindBlob(3, info.bag_of_chips.data(), info.bag_of_chips.size()); | 233 s1.BindBlob(3, info.bag_of_chips.data(), info.bag_of_chips.size()); |
231 | 234 |
232 if (!s1.Run()) | 235 if (!s1.Run()) |
233 return false; | 236 return false; |
234 DCHECK_EQ(db_->GetLastChangeCount(), 1); | 237 DCHECK_EQ(db_->GetLastChangeCount(), 1); |
235 | 238 |
236 sql::Statement s2(db_->GetCachedStatement( | 239 sql::Statement s2(db_->GetCachedStatement( |
237 SQL_FROM_HERE, | 240 SQL_FROM_HERE, |
238 "INSERT OR REPLACE " | 241 "INSERT OR REPLACE " |
239 "INTO models (model_id, progress_marker, initial_sync_ended, " | 242 "INTO models (model_id, progress_marker, transaction_version) " |
240 " transaction_version) " | 243 "VALUES (?, ?, ?)")); |
241 "VALUES (?, ?, ?, ?)")); | |
242 | 244 |
243 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { | 245 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { |
244 // We persist not ModelType but rather a protobuf-derived ID. | 246 // We persist not ModelType but rather a protobuf-derived ID. |
245 string model_id = ModelTypeEnumToModelId(ModelTypeFromInt(i)); | 247 string model_id = ModelTypeEnumToModelId(ModelTypeFromInt(i)); |
246 string progress_marker; | 248 string progress_marker; |
247 info.download_progress[i].SerializeToString(&progress_marker); | 249 info.download_progress[i].SerializeToString(&progress_marker); |
248 s2.BindBlob(0, model_id.data(), model_id.length()); | 250 s2.BindBlob(0, model_id.data(), model_id.length()); |
249 s2.BindBlob(1, progress_marker.data(), progress_marker.length()); | 251 s2.BindBlob(1, progress_marker.data(), progress_marker.length()); |
250 s2.BindBool(2, info.initial_sync_ended.Has(ModelTypeFromInt(i))); | 252 s2.BindInt64(2, info.transaction_version[i]); |
251 s2.BindInt64(3, info.transaction_version[i]); | |
252 if (!s2.Run()) | 253 if (!s2.Run()) |
253 return false; | 254 return false; |
254 DCHECK_EQ(db_->GetLastChangeCount(), 1); | 255 DCHECK_EQ(db_->GetLastChangeCount(), 1); |
255 s2.Reset(true); | 256 s2.Reset(true); |
256 } | 257 } |
257 } | 258 } |
258 | 259 |
259 return transaction.Commit(); | 260 return transaction.Commit(); |
260 } | 261 } |
261 | 262 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 if (MigrateVersion82To83()) | 364 if (MigrateVersion82To83()) |
364 version_on_disk = 83; | 365 version_on_disk = 83; |
365 } | 366 } |
366 | 367 |
367 // Version 84 migration added deleted_metas table. | 368 // Version 84 migration added deleted_metas table. |
368 if (version_on_disk == 83) { | 369 if (version_on_disk == 83) { |
369 if (MigrateVersion83To84()) | 370 if (MigrateVersion83To84()) |
370 version_on_disk = 84; | 371 version_on_disk = 84; |
371 } | 372 } |
372 | 373 |
| 374 // Version 85 migration removes the initial_sync_ended bits. |
| 375 if (version_on_disk == 84) { |
| 376 if (MigrateVersion84To85()) |
| 377 version_on_disk = 85; |
| 378 } |
| 379 |
373 // If one of the migrations requested it, drop columns that aren't current. | 380 // If one of the migrations requested it, drop columns that aren't current. |
374 // It's only safe to do this after migrating all the way to the current | 381 // It's only safe to do this after migrating all the way to the current |
375 // version. | 382 // version. |
376 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { | 383 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { |
377 if (!RefreshColumns()) | 384 if (!RefreshColumns()) |
378 version_on_disk = 0; | 385 version_on_disk = 0; |
379 } | 386 } |
380 | 387 |
381 // A final, alternative catch-all migration to simply re-sync everything. | 388 // A final, alternative catch-all migration to simply re-sync everything. |
382 // | |
383 // TODO(rlarocque): It's wrong to recreate the database here unless the higher | |
384 // layers were expecting us to do so. See crbug.com/103824. We must leave | |
385 // this code as is for now because this is the code that ends up creating the | |
386 // database in the first time sync case, where the higher layers are expecting | |
387 // us to create a fresh database. The solution to this should be to implement | |
388 // crbug.com/105018. | |
389 if (version_on_disk != kCurrentDBVersion) { | 389 if (version_on_disk != kCurrentDBVersion) { |
390 if (version_on_disk > kCurrentDBVersion) | 390 if (version_on_disk > kCurrentDBVersion) |
391 return false; | 391 return false; |
392 | 392 |
393 // Fallback (re-sync everything) migration path. | 393 // Fallback (re-sync everything) migration path. |
394 DVLOG(1) << "Old/null sync database, version " << version_on_disk; | 394 DVLOG(1) << "Old/null sync database, version " << version_on_disk; |
395 // Delete the existing database (if any), and create a fresh one. | 395 // Delete the existing database (if any), and create a fresh one. |
396 DropAllTables(); | 396 DropAllTables(); |
397 if (!CreateTables()) | 397 if (!CreateTables()) |
398 return false; | 398 return false; |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
501 s.ColumnBlobAsString(4, &(info->kernel_info.bag_of_chips)); | 501 s.ColumnBlobAsString(4, &(info->kernel_info.bag_of_chips)); |
502 | 502 |
503 // Verify there was only one row returned. | 503 // Verify there was only one row returned. |
504 DCHECK(!s.Step()); | 504 DCHECK(!s.Step()); |
505 DCHECK(s.Succeeded()); | 505 DCHECK(s.Succeeded()); |
506 } | 506 } |
507 | 507 |
508 { | 508 { |
509 sql::Statement s( | 509 sql::Statement s( |
510 db_->GetUniqueStatement( | 510 db_->GetUniqueStatement( |
511 "SELECT model_id, progress_marker, initial_sync_ended, " | 511 "SELECT model_id, progress_marker, " |
512 "transaction_version FROM models")); | 512 "transaction_version FROM models")); |
513 | 513 |
514 while (s.Step()) { | 514 while (s.Step()) { |
515 ModelType type = ModelIdToModelTypeEnum(s.ColumnBlob(0), | 515 ModelType type = ModelIdToModelTypeEnum(s.ColumnBlob(0), |
516 s.ColumnByteLength(0)); | 516 s.ColumnByteLength(0)); |
517 if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) { | 517 if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) { |
518 info->kernel_info.download_progress[type].ParseFromArray( | 518 info->kernel_info.download_progress[type].ParseFromArray( |
519 s.ColumnBlob(1), s.ColumnByteLength(1)); | 519 s.ColumnBlob(1), s.ColumnByteLength(1)); |
520 if (s.ColumnBool(2)) | 520 info->kernel_info.transaction_version[type] = s.ColumnInt64(2); |
521 info->kernel_info.initial_sync_ended.Put(type); | |
522 info->kernel_info.transaction_version[type] = s.ColumnInt64(3); | |
523 } | 521 } |
524 } | 522 } |
525 if (!s.Succeeded()) | 523 if (!s.Succeeded()) |
526 return false; | 524 return false; |
527 } | 525 } |
528 { | 526 { |
529 sql::Statement s( | 527 sql::Statement s( |
530 db_->GetUniqueStatement( | 528 db_->GetUniqueStatement( |
531 "SELECT MAX(metahandle) FROM metas")); | 529 "SELECT MAX(metahandle) FROM metas")); |
532 if (!s.Step()) | 530 if (!s.Step()) |
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
907 // boolean initial_sync_ended | 905 // boolean initial_sync_ended |
908 // In version 75, we deprecated the integer-valued last_download_timestamp, | 906 // In version 75, we deprecated the integer-valued last_download_timestamp, |
909 // using insted a protobuf-valued progress_marker field: | 907 // using insted a protobuf-valued progress_marker field: |
910 // blob progress_marker | 908 // blob progress_marker |
911 // The progress_marker values are initialized from the value of | 909 // The progress_marker values are initialized from the value of |
912 // last_download_timestamp, thereby preserving the download state. | 910 // last_download_timestamp, thereby preserving the download state. |
913 | 911 |
914 // Move aside the old table and create a new empty one at the current schema. | 912 // Move aside the old table and create a new empty one at the current schema. |
915 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) | 913 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) |
916 return false; | 914 return false; |
917 if (!CreateModelsTable()) | 915 if (!CreateV75ModelsTable()) |
918 return false; | 916 return false; |
919 | 917 |
920 sql::Statement query(db_->GetUniqueStatement( | 918 sql::Statement query(db_->GetUniqueStatement( |
921 "SELECT model_id, last_download_timestamp, initial_sync_ended " | 919 "SELECT model_id, last_download_timestamp, initial_sync_ended " |
922 "FROM temp_models")); | 920 "FROM temp_models")); |
923 | 921 |
924 sql::Statement update(db_->GetUniqueStatement( | 922 sql::Statement update(db_->GetUniqueStatement( |
925 "INSERT INTO models (model_id, " | 923 "INSERT INTO models (model_id, " |
926 "progress_marker, initial_sync_ended) VALUES (?, ?, ?)")); | 924 "progress_marker, initial_sync_ended) VALUES (?, ?, ?)")); |
927 | 925 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1058 return false; | 1056 return false; |
1059 put_ordinals.Reset(true); | 1057 put_ordinals.Reset(true); |
1060 } | 1058 } |
1061 | 1059 |
1062 SetVersion(81); | 1060 SetVersion(81); |
1063 needs_column_refresh_ = true; | 1061 needs_column_refresh_ = true; |
1064 return true; | 1062 return true; |
1065 } | 1063 } |
1066 | 1064 |
1067 bool DirectoryBackingStore::MigrateVersion81To82() { | 1065 bool DirectoryBackingStore::MigrateVersion81To82() { |
1068 // Version 82 added transaction_version to kernel info. But if user is | |
1069 // migrating from 74 or before, 74->75 migration would recreate models table | |
1070 // that already has transaction_version column. | |
1071 if (db_->DoesColumnExist("models", "transaction_version")) { | |
1072 SetVersion(82); | |
1073 return true; | |
1074 } | |
1075 | |
1076 if (!db_->Execute( | 1066 if (!db_->Execute( |
1077 "ALTER TABLE models ADD COLUMN transaction_version BIGINT default 0")) | 1067 "ALTER TABLE models ADD COLUMN transaction_version BIGINT default 0")) |
1078 return false; | 1068 return false; |
1079 sql::Statement update(db_->GetUniqueStatement( | 1069 sql::Statement update(db_->GetUniqueStatement( |
1080 "UPDATE models SET transaction_version = 0")); | 1070 "UPDATE models SET transaction_version = 0")); |
1081 if (!update.Run()) | 1071 if (!update.Run()) |
1082 return false; | 1072 return false; |
1083 SetVersion(82); | 1073 SetVersion(82); |
1084 return true; | 1074 return true; |
1085 } | 1075 } |
(...skipping 11 matching lines...) Expand all Loading... |
1097 return true; | 1087 return true; |
1098 } | 1088 } |
1099 | 1089 |
1100 bool DirectoryBackingStore::MigrateVersion83To84() { | 1090 bool DirectoryBackingStore::MigrateVersion83To84() { |
1101 // Version 84 added deleted_metas table to store deleted metas until we know | 1091 // Version 84 added deleted_metas table to store deleted metas until we know |
1102 // for sure that the deletions are persisted in native models. | 1092 // for sure that the deletions are persisted in native models. |
1103 string query = "CREATE TABLE deleted_metas "; | 1093 string query = "CREATE TABLE deleted_metas "; |
1104 query.append(ComposeCreateTableColumnSpecs()); | 1094 query.append(ComposeCreateTableColumnSpecs()); |
1105 if (!db_->Execute(query.c_str())) | 1095 if (!db_->Execute(query.c_str())) |
1106 return false; | 1096 return false; |
| 1097 |
1107 SetVersion(84); | 1098 SetVersion(84); |
1108 return true; | 1099 return true; |
1109 } | 1100 } |
1110 | 1101 |
| 1102 bool DirectoryBackingStore::MigrateVersion84To85() { |
| 1103 // Version 84 removes the initial_sync_ended flag. |
| 1104 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) |
| 1105 return false; |
| 1106 if (!CreateModelsTable()) |
| 1107 return false; |
| 1108 if (!db_->Execute("INSERT INTO models SELECT " |
| 1109 "model_id, progress_marker, transaction_version " |
| 1110 "FROM temp_models")) { |
| 1111 return false; |
| 1112 } |
| 1113 SafeDropTable("temp_models"); |
| 1114 |
| 1115 SetVersion(85); |
| 1116 return true; |
| 1117 } |
| 1118 |
1111 bool DirectoryBackingStore::CreateTables() { | 1119 bool DirectoryBackingStore::CreateTables() { |
1112 DVLOG(1) << "First run, creating tables"; | 1120 DVLOG(1) << "First run, creating tables"; |
1113 // Create two little tables share_version and share_info | 1121 // Create two little tables share_version and share_info |
1114 if (!db_->Execute( | 1122 if (!db_->Execute( |
1115 "CREATE TABLE share_version (" | 1123 "CREATE TABLE share_version (" |
1116 "id VARCHAR(128) primary key, data INT)")) { | 1124 "id VARCHAR(128) primary key, data INT)")) { |
1117 return false; | 1125 return false; |
1118 } | 1126 } |
1119 | 1127 |
1120 { | 1128 { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1206 return db_->Execute( | 1214 return db_->Execute( |
1207 "CREATE TABLE models (" | 1215 "CREATE TABLE models (" |
1208 "model_id BLOB primary key, " | 1216 "model_id BLOB primary key, " |
1209 "last_download_timestamp INT, " | 1217 "last_download_timestamp INT, " |
1210 // Gets set if the syncer ever gets updates from the | 1218 // Gets set if the syncer ever gets updates from the |
1211 // server and the server returns 0. Lets us detect the | 1219 // server and the server returns 0. Lets us detect the |
1212 // end of the initial sync. | 1220 // end of the initial sync. |
1213 "initial_sync_ended BOOLEAN default 0)"); | 1221 "initial_sync_ended BOOLEAN default 0)"); |
1214 } | 1222 } |
1215 | 1223 |
| 1224 bool DirectoryBackingStore::CreateV75ModelsTable() { |
| 1225 // This is an old schema for the Models table, used from versions 75 to 80. |
| 1226 return db_->Execute( |
| 1227 "CREATE TABLE models (" |
| 1228 "model_id BLOB primary key, " |
| 1229 "progress_marker BLOB, " |
| 1230 // Gets set if the syncer ever gets updates from the |
| 1231 // server and the server returns 0. Lets us detect the |
| 1232 // end of the initial sync. |
| 1233 "initial_sync_ended BOOLEAN default 0)"); |
| 1234 } |
| 1235 |
1216 bool DirectoryBackingStore::CreateModelsTable() { | 1236 bool DirectoryBackingStore::CreateModelsTable() { |
1217 // This is the current schema for the Models table, from version 81 | 1237 // This is the current schema for the Models table, from version 81 |
1218 // onward. If you change the schema, you'll probably want to double-check | 1238 // onward. If you change the schema, you'll probably want to double-check |
1219 // the use of this function in the v74-v75 migration. | 1239 // the use of this function in the v84-v85 migration. |
1220 return db_->Execute( | 1240 return db_->Execute( |
1221 "CREATE TABLE models (" | 1241 "CREATE TABLE models (" |
1222 "model_id BLOB primary key, " | 1242 "model_id BLOB primary key, " |
1223 "progress_marker BLOB, " | 1243 "progress_marker BLOB, " |
1224 // Gets set if the syncer ever gets updates from the | 1244 // Gets set if the syncer ever gets updates from the |
1225 // server and the server returns 0. Lets us detect the | 1245 // server and the server returns 0. Lets us detect the |
1226 // end of the initial sync. | 1246 // end of the initial sync. |
1227 "initial_sync_ended BOOLEAN default 0, " | |
1228 "transaction_version BIGINT default 0)"); | 1247 "transaction_version BIGINT default 0)"); |
1229 } | 1248 } |
1230 | 1249 |
1231 bool DirectoryBackingStore::CreateShareInfoTable(bool is_temporary) { | 1250 bool DirectoryBackingStore::CreateShareInfoTable(bool is_temporary) { |
1232 const char* name = is_temporary ? "temp_share_info" : "share_info"; | 1251 const char* name = is_temporary ? "temp_share_info" : "share_info"; |
1233 string query = "CREATE TABLE "; | 1252 string query = "CREATE TABLE "; |
1234 query.append(name); | 1253 query.append(name); |
1235 // This is the current schema for the ShareInfo table, from version 76 | 1254 // This is the current schema for the ShareInfo table, from version 76 |
1236 // onward. | 1255 // onward. |
1237 query.append(" (" | 1256 query.append(" (" |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1291 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); | 1310 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); |
1292 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); | 1311 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); |
1293 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); | 1312 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); |
1294 is_ok = is_ok && prev_exists && parent_exists && next_exists; | 1313 is_ok = is_ok && prev_exists && parent_exists && next_exists; |
1295 } | 1314 } |
1296 return is_ok; | 1315 return is_ok; |
1297 } | 1316 } |
1298 | 1317 |
1299 } // namespace syncable | 1318 } // namespace syncable |
1300 } // namespace syncer | 1319 } // namespace syncer |
OLD | NEW |