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