OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "chrome/browser/sync/syncable/directory_backing_store.h" | 5 #include "chrome/browser/sync/syncable/directory_backing_store.h" |
6 | 6 |
7 #include "build/build_config.h" | 7 #include "build/build_config.h" |
8 | 8 |
9 #if defined(OS_MACOSX) | |
10 #include <CoreFoundation/CoreFoundation.h> | |
11 #endif | |
12 | |
13 #include <limits> | 9 #include <limits> |
14 | 10 |
15 #include "base/file_util.h" | 11 #include "base/file_util.h" |
16 #include "base/hash_tables.h" | 12 #include "base/hash_tables.h" |
17 #include "base/logging.h" | 13 #include "base/logging.h" |
18 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
19 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
20 #include "base/string_number_conversions.h" | 16 #include "base/string_number_conversions.h" |
21 #include "base/stringprintf.h" | 17 #include "base/stringprintf.h" |
18 #include "base/time.h" | |
22 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" | 19 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" |
23 #include "chrome/browser/sync/protocol/service_constants.h" | 20 #include "chrome/browser/sync/protocol/service_constants.h" |
24 #include "chrome/browser/sync/protocol/sync.pb.h" | 21 #include "chrome/browser/sync/protocol/sync.pb.h" |
25 #include "chrome/browser/sync/syncable/syncable-inl.h" | 22 #include "chrome/browser/sync/syncable/syncable-inl.h" |
26 #include "chrome/browser/sync/syncable/syncable_columns.h" | 23 #include "chrome/browser/sync/syncable/syncable_columns.h" |
27 #include "chrome/browser/sync/util/sqlite_utils.h" | 24 #include "chrome/browser/sync/util/sqlite_utils.h" |
25 #include "chrome/browser/sync/util/time.h" | |
28 #include "chrome/common/random.h" | 26 #include "chrome/common/random.h" |
29 #include "third_party/sqlite/sqlite3.h" | 27 #include "third_party/sqlite/sqlite3.h" |
30 | 28 |
31 // Sometimes threads contend on the DB lock itself, especially when one thread | 29 // Sometimes threads contend on the DB lock itself, especially when one thread |
32 // is calling SaveChanges. In the worst case scenario, the user can put his | 30 // is calling SaveChanges. In the worst case scenario, the user can put his |
33 // laptop to sleep during db contention, and wake up the laptop days later, so | 31 // laptop to sleep during db contention, and wake up the laptop days later, so |
34 // infinity seems like the best choice here. | 32 // infinity seems like the best choice here. |
35 const int kDirectoryBackingStoreBusyTimeoutMs = std::numeric_limits<int>::max(); | 33 const int kDirectoryBackingStoreBusyTimeoutMs = std::numeric_limits<int>::max(); |
36 | 34 |
37 using std::string; | 35 using std::string; |
38 | 36 |
39 namespace syncable { | 37 namespace syncable { |
40 | 38 |
41 // This just has to be big enough to hold an UPDATE or INSERT statement that | 39 // This just has to be big enough to hold an UPDATE or INSERT statement that |
42 // modifies all the columns in the entry table. | 40 // modifies all the columns in the entry table. |
43 static const string::size_type kUpdateStatementBufferSize = 2048; | 41 static const string::size_type kUpdateStatementBufferSize = 2048; |
44 | 42 |
45 // Increment this version whenever updating DB tables. | 43 // Increment this version whenever updating DB tables. |
46 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. | 44 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. |
47 const int32 kCurrentDBVersion = 76; | 45 const int32 kCurrentDBVersion = 77; |
48 | 46 |
49 namespace { | 47 namespace { |
50 | 48 |
51 int ExecQuery(sqlite3* dbhandle, const char* query) { | 49 int ExecQuery(sqlite3* dbhandle, const char* query) { |
52 sqlite_utils::SQLStatement statement; | 50 sqlite_utils::SQLStatement statement; |
53 int result = statement.prepare(dbhandle, query); | 51 int result = statement.prepare(dbhandle, query); |
54 if (SQLITE_OK != result) | 52 if (SQLITE_OK != result) |
55 return result; | 53 return result; |
56 do { | 54 do { |
57 result = statement.step(); | 55 result = statement.step(); |
(...skipping 11 matching lines...) Expand all Loading... | |
69 | 67 |
70 // Iterate over the fields of |entry| and bind each to |statement| for | 68 // Iterate over the fields of |entry| and bind each to |statement| for |
71 // updating. Returns the number of args bound. | 69 // updating. Returns the number of args bound. |
72 int BindFields(const EntryKernel& entry, | 70 int BindFields(const EntryKernel& entry, |
73 sqlite_utils::SQLStatement* statement) { | 71 sqlite_utils::SQLStatement* statement) { |
74 int index = 0; | 72 int index = 0; |
75 int i = 0; | 73 int i = 0; |
76 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { | 74 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { |
77 statement->bind_int64(index++, entry.ref(static_cast<Int64Field>(i))); | 75 statement->bind_int64(index++, entry.ref(static_cast<Int64Field>(i))); |
78 } | 76 } |
77 for ( ; i < TIME_FIELDS_END; ++i) { | |
78 statement->bind_int64(index++, | |
79 browser_sync::TimeToProtoTime( | |
80 entry.ref(static_cast<TimeField>(i)))); | |
81 } | |
79 for ( ; i < ID_FIELDS_END; ++i) { | 82 for ( ; i < ID_FIELDS_END; ++i) { |
80 statement->bind_string(index++, entry.ref(static_cast<IdField>(i)).s_); | 83 statement->bind_string(index++, entry.ref(static_cast<IdField>(i)).s_); |
81 } | 84 } |
82 for ( ; i < BIT_FIELDS_END; ++i) { | 85 for ( ; i < BIT_FIELDS_END; ++i) { |
83 statement->bind_int(index++, entry.ref(static_cast<BitField>(i))); | 86 statement->bind_int(index++, entry.ref(static_cast<BitField>(i))); |
84 } | 87 } |
85 for ( ; i < STRING_FIELDS_END; ++i) { | 88 for ( ; i < STRING_FIELDS_END; ++i) { |
86 statement->bind_string(index++, entry.ref(static_cast<StringField>(i))); | 89 statement->bind_string(index++, entry.ref(static_cast<StringField>(i))); |
87 } | 90 } |
88 std::string temp; | 91 std::string temp; |
89 for ( ; i < PROTO_FIELDS_END; ++i) { | 92 for ( ; i < PROTO_FIELDS_END; ++i) { |
90 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp); | 93 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp); |
91 statement->bind_blob(index++, temp.data(), temp.length()); | 94 statement->bind_blob(index++, temp.data(), temp.length()); |
92 } | 95 } |
93 return index; | 96 return index; |
94 } | 97 } |
95 | 98 |
96 // The caller owns the returned EntryKernel*. | 99 // The caller owns the returned EntryKernel*. |
97 int UnpackEntry(sqlite_utils::SQLStatement* statement, EntryKernel** kernel) { | 100 int UnpackEntry(sqlite_utils::SQLStatement* statement, EntryKernel** kernel) { |
98 *kernel = NULL; | 101 *kernel = NULL; |
99 int query_result = statement->step(); | 102 int query_result = statement->step(); |
100 if (SQLITE_ROW == query_result) { | 103 if (SQLITE_ROW == query_result) { |
101 *kernel = new EntryKernel; | 104 *kernel = new EntryKernel; |
102 (*kernel)->clear_dirty(NULL); | 105 (*kernel)->clear_dirty(NULL); |
103 DCHECK(statement->column_count() == static_cast<int>(FIELD_COUNT)); | 106 DCHECK(statement->column_count() == static_cast<int>(FIELD_COUNT)); |
104 int i = 0; | 107 int i = 0; |
105 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { | 108 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { |
106 (*kernel)->put(static_cast<Int64Field>(i), statement->column_int64(i)); | 109 (*kernel)->put(static_cast<Int64Field>(i), statement->column_int64(i)); |
107 } | 110 } |
111 for ( ; i < TIME_FIELDS_END; ++i) { | |
112 (*kernel)->put(static_cast<TimeField>(i), | |
113 browser_sync::ProtoTimeToTime( | |
114 statement->column_int64(i))); | |
115 } | |
108 for ( ; i < ID_FIELDS_END; ++i) { | 116 for ( ; i < ID_FIELDS_END; ++i) { |
109 (*kernel)->mutable_ref(static_cast<IdField>(i)).s_ = | 117 (*kernel)->mutable_ref(static_cast<IdField>(i)).s_ = |
110 statement->column_string(i); | 118 statement->column_string(i); |
111 } | 119 } |
112 for ( ; i < BIT_FIELDS_END; ++i) { | 120 for ( ; i < BIT_FIELDS_END; ++i) { |
113 (*kernel)->put(static_cast<BitField>(i), (0 != statement->column_int(i))); | 121 (*kernel)->put(static_cast<BitField>(i), (0 != statement->column_int(i))); |
114 } | 122 } |
115 for ( ; i < STRING_FIELDS_END; ++i) { | 123 for ( ; i < STRING_FIELDS_END; ++i) { |
116 (*kernel)->put(static_cast<StringField>(i), | 124 (*kernel)->put(static_cast<StringField>(i), |
117 statement->column_string(i)); | 125 statement->column_string(i)); |
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
471 if (MigrateVersion74To75()) | 479 if (MigrateVersion74To75()) |
472 version_on_disk = 75; | 480 version_on_disk = 75; |
473 } | 481 } |
474 | 482 |
475 // Version 76 removed all (5) autofill migration related columns. | 483 // Version 76 removed all (5) autofill migration related columns. |
476 if (version_on_disk == 75) { | 484 if (version_on_disk == 75) { |
477 if (MigrateVersion75To76()) | 485 if (MigrateVersion75To76()) |
478 version_on_disk = 76; | 486 version_on_disk = 76; |
479 } | 487 } |
480 | 488 |
489 // Version 77 standardized all time fields to Unix epoch times. | |
490 if (version_on_disk == 76) { | |
491 if (MigrateVersion76To77()) | |
492 version_on_disk = 77; | |
493 } | |
494 | |
481 // If one of the migrations requested it, drop columns that aren't current. | 495 // If one of the migrations requested it, drop columns that aren't current. |
482 // It's only safe to do this after migrating all the way to the current | 496 // It's only safe to do this after migrating all the way to the current |
483 // version. | 497 // version. |
484 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { | 498 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { |
485 if (!RefreshColumns()) | 499 if (!RefreshColumns()) |
486 version_on_disk = 0; | 500 version_on_disk = 0; |
487 } | 501 } |
488 | 502 |
489 // A final, alternative catch-all migration to simply re-sync everything. | 503 // A final, alternative catch-all migration to simply re-sync everything. |
490 if (version_on_disk != kCurrentDBVersion) { | 504 if (version_on_disk != kCurrentDBVersion) { |
(...skipping 591 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1082 // bookmarks_added_during_autofill_migration | 1096 // bookmarks_added_during_autofill_migration |
1083 // autofill_migration_time | 1097 // autofill_migration_time |
1084 // autofill_entries_added_during_migration | 1098 // autofill_entries_added_during_migration |
1085 // autofill_profiles_added_during_migration | 1099 // autofill_profiles_added_during_migration |
1086 // No data migration is necessary, but we should do a column refresh. | 1100 // No data migration is necessary, but we should do a column refresh. |
1087 SetVersion(76); | 1101 SetVersion(76); |
1088 needs_column_refresh_ = true; | 1102 needs_column_refresh_ = true; |
1089 return true; | 1103 return true; |
1090 } | 1104 } |
1091 | 1105 |
1106 bool DirectoryBackingStore::MigrateVersion76To77() { | |
1107 sqlite_utils::SQLStatement update_timestamps; | |
1108 // This change changes the format of stored timestamps to ms since | |
1109 // the Unix epoch. | |
1110 #if defined(OS_WIN) | |
1111 // On Windows, we used to store timestamps in FILETIME format. Magic | |
Nicolas Zea
2011/09/21 18:09:12
may as well mention: (100's of ns since Jan 1 1601
akalin
2011/09/21 19:37:35
Done.
| |
1112 // numbers taken from | |
1113 // http://stackoverflow.com/questions/5398557/java-library-for-dealing-with-win3 2-filetime . | |
1114 #define TO_UNIX_TIME_MS(x) #x " = " #x " / 10000 - 11644473600000" | |
1115 #else | |
1116 // On other platforms, we used to store timestamps in time_t format (s | |
1117 // since the Unix epoch). | |
1118 #define TO_UNIX_TIME_MS(x) #x " = " #x " * 1000" | |
1119 #endif | |
1120 update_timestamps.prepare( | |
1121 load_dbhandle_, | |
1122 "UPDATE metas SET " | |
1123 TO_UNIX_TIME_MS(mtime) ", " | |
1124 TO_UNIX_TIME_MS(server_mtime) ", " | |
1125 TO_UNIX_TIME_MS(ctime) ", " | |
1126 TO_UNIX_TIME_MS(server_ctime)); | |
1127 #undef TO_UNIX_TIME_MS | |
1128 if (update_timestamps.step() != SQLITE_DONE) | |
1129 return false; | |
1130 SetVersion(77); | |
1131 return true; | |
1132 } | |
1133 | |
1092 int DirectoryBackingStore::CreateTables() { | 1134 int DirectoryBackingStore::CreateTables() { |
1093 VLOG(1) << "First run, creating tables"; | 1135 VLOG(1) << "First run, creating tables"; |
1094 // Create two little tables share_version and share_info | 1136 // Create two little tables share_version and share_info |
1095 int result = ExecQuery(load_dbhandle_, | 1137 int result = ExecQuery(load_dbhandle_, |
1096 "CREATE TABLE share_version (" | 1138 "CREATE TABLE share_version (" |
1097 "id VARCHAR(128) primary key, data INT)"); | 1139 "id VARCHAR(128) primary key, data INT)"); |
1098 if (result != SQLITE_DONE) | 1140 if (result != SQLITE_DONE) |
1099 return result; | 1141 return result; |
1100 { | 1142 { |
1101 sqlite_utils::SQLStatement statement; | 1143 sqlite_utils::SQLStatement statement; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1136 | 1178 |
1137 result = CreateModelsTable(); | 1179 result = CreateModelsTable(); |
1138 if (result != SQLITE_DONE) | 1180 if (result != SQLITE_DONE) |
1139 return result; | 1181 return result; |
1140 // Create the big metas table. | 1182 // Create the big metas table. |
1141 result = CreateMetasTable(false); | 1183 result = CreateMetasTable(false); |
1142 if (result != SQLITE_DONE) | 1184 if (result != SQLITE_DONE) |
1143 return result; | 1185 return result; |
1144 { | 1186 { |
1145 // Insert the entry for the root into the metas table. | 1187 // Insert the entry for the root into the metas table. |
1146 const int64 now = Now(); | 1188 const int64 now = browser_sync::TimeToProtoTime(base::Time::Now()); |
1147 sqlite_utils::SQLStatement statement; | 1189 sqlite_utils::SQLStatement statement; |
1148 statement.prepare(load_dbhandle_, | 1190 statement.prepare(load_dbhandle_, |
1149 "INSERT INTO metas " | 1191 "INSERT INTO metas " |
1150 "( id, metahandle, is_dir, ctime, mtime) " | 1192 "( id, metahandle, is_dir, ctime, mtime) " |
1151 "VALUES ( \"r\", 1, 1, ?, ?)"); | 1193 "VALUES ( \"r\", 1, 1, ?, ?)"); |
1152 statement.bind_int64(0, now); | 1194 statement.bind_int64(0, now); |
1153 statement.bind_int64(1, now); | 1195 statement.bind_int64(1, now); |
1154 result = statement.step(); | 1196 result = statement.step(); |
1155 } | 1197 } |
1156 return result; | 1198 return result; |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1228 "id TEXT primary key, " | 1270 "id TEXT primary key, " |
1229 "name TEXT, " | 1271 "name TEXT, " |
1230 "store_birthday TEXT, " | 1272 "store_birthday TEXT, " |
1231 "db_create_version TEXT, " | 1273 "db_create_version TEXT, " |
1232 "db_create_time INT, " | 1274 "db_create_time INT, " |
1233 "next_id INT default -2, " | 1275 "next_id INT default -2, " |
1234 "cache_guid TEXT )"); | 1276 "cache_guid TEXT )"); |
1235 return ExecQuery(load_dbhandle_, query.c_str()); | 1277 return ExecQuery(load_dbhandle_, query.c_str()); |
1236 } | 1278 } |
1237 } // namespace syncable | 1279 } // namespace syncable |
OLD | NEW |