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 |
11 #include "base/base64.h" | 11 #include "base/base64.h" |
12 #include "base/debug/trace_event.h" | 12 #include "base/debug/trace_event.h" |
13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
14 #include "base/hash_tables.h" | 14 #include "base/hash_tables.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/metrics/histogram.h" | 16 #include "base/metrics/histogram.h" |
17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
| 18 #include "base/sha1.h" |
18 #include "base/stl_util.h" | 19 #include "base/stl_util.h" |
19 #include "base/string_number_conversions.h" | 20 #include "base/string_number_conversions.h" |
20 #include "base/stringprintf.h" | 21 #include "base/stringprintf.h" |
21 #include "base/time.h" | 22 #include "base/time.h" |
22 #include "sql/connection.h" | 23 #include "sql/connection.h" |
23 #include "sql/statement.h" | 24 #include "sql/statement.h" |
24 #include "sql/transaction.h" | 25 #include "sql/transaction.h" |
25 #include "sync/internal_api/public/base/node_ordinal.h" | 26 #include "sync/internal_api/public/base/node_ordinal.h" |
26 #include "sync/protocol/bookmark_specifics.pb.h" | 27 #include "sync/protocol/bookmark_specifics.pb.h" |
27 #include "sync/protocol/sync.pb.h" | 28 #include "sync/protocol/sync.pb.h" |
28 #include "sync/syncable/syncable-inl.h" | 29 #include "sync/syncable/syncable-inl.h" |
29 #include "sync/syncable/syncable_columns.h" | 30 #include "sync/syncable/syncable_columns.h" |
30 #include "sync/util/time.h" | 31 #include "sync/util/time.h" |
31 | 32 |
32 using std::string; | 33 using std::string; |
33 | 34 |
34 namespace syncer { | 35 namespace syncer { |
35 namespace syncable { | 36 namespace syncable { |
36 | 37 |
37 // This just has to be big enough to hold an UPDATE or INSERT statement that | 38 // This just has to be big enough to hold an UPDATE or INSERT statement that |
38 // modifies all the columns in the entry table. | 39 // modifies all the columns in the entry table. |
39 static const string::size_type kUpdateStatementBufferSize = 2048; | 40 static const string::size_type kUpdateStatementBufferSize = 2048; |
40 | 41 |
41 // Increment this version whenever updating DB tables. | 42 // Increment this version whenever updating DB tables. |
42 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. | 43 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. |
43 const int32 kCurrentDBVersion = 85; | 44 const int32 kCurrentDBVersion = 86; |
44 | 45 |
45 // Iterate over the fields of |entry| and bind each to |statement| for | 46 // Iterate over the fields of |entry| and bind each to |statement| for |
46 // updating. Returns the number of args bound. | 47 // updating. Returns the number of args bound. |
47 void BindFields(const EntryKernel& entry, | 48 void BindFields(const EntryKernel& entry, |
48 sql::Statement* statement) { | 49 sql::Statement* statement) { |
49 int index = 0; | 50 int index = 0; |
50 int i = 0; | 51 int i = 0; |
51 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { | 52 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { |
52 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i))); | 53 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i))); |
53 } | 54 } |
54 for ( ; i < TIME_FIELDS_END; ++i) { | 55 for ( ; i < TIME_FIELDS_END; ++i) { |
55 statement->BindInt64(index++, | 56 statement->BindInt64(index++, |
56 TimeToProtoTime( | 57 TimeToProtoTime( |
57 entry.ref(static_cast<TimeField>(i)))); | 58 entry.ref(static_cast<TimeField>(i)))); |
58 } | 59 } |
59 for ( ; i < ID_FIELDS_END; ++i) { | 60 for ( ; i < ID_FIELDS_END; ++i) { |
60 statement->BindString(index++, entry.ref(static_cast<IdField>(i)).s_); | 61 statement->BindString(index++, entry.ref(static_cast<IdField>(i)).s_); |
61 } | 62 } |
62 for ( ; i < BIT_FIELDS_END; ++i) { | 63 for ( ; i < BIT_FIELDS_END; ++i) { |
63 statement->BindInt(index++, entry.ref(static_cast<BitField>(i))); | 64 statement->BindInt(index++, entry.ref(static_cast<BitField>(i))); |
64 } | 65 } |
65 for ( ; i < STRING_FIELDS_END; ++i) { | 66 for ( ; i < STRING_FIELDS_END; ++i) { |
66 statement->BindString(index++, entry.ref(static_cast<StringField>(i))); | 67 statement->BindString(index++, entry.ref(static_cast<StringField>(i))); |
67 } | 68 } |
68 std::string temp; | |
69 for ( ; i < PROTO_FIELDS_END; ++i) { | 69 for ( ; i < PROTO_FIELDS_END; ++i) { |
| 70 std::string temp; |
70 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp); | 71 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp); |
71 statement->BindBlob(index++, temp.data(), temp.length()); | 72 statement->BindBlob(index++, temp.data(), temp.length()); |
72 } | 73 } |
73 for( ; i < ORDINAL_FIELDS_END; ++i) { | 74 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) { |
74 temp = entry.ref(static_cast<OrdinalField>(i)).ToInternalValue(); | 75 const std::string& temp = |
| 76 entry.ref(static_cast<UniquePositionField>(i)).ToInternalValue(); |
| 77 statement->BindBlob(index++, temp.data(), temp.length()); |
| 78 } |
| 79 for ( ; i < BYTES_FIELDS_END; ++i) { |
| 80 const std::string& temp = entry.ref(static_cast<BytesField>(i)); |
75 statement->BindBlob(index++, temp.data(), temp.length()); | 81 statement->BindBlob(index++, temp.data(), temp.length()); |
76 } | 82 } |
77 } | 83 } |
78 | 84 |
79 // The caller owns the returned EntryKernel*. Assumes the statement currently | 85 // The caller owns the returned EntryKernel*. Assumes the statement currently |
80 // points to a valid row in the metas table. Returns NULL to indicate that | 86 // points to a valid row in the metas table. Returns NULL to indicate that |
81 // it detected a corruption in the data on unpacking. | 87 // it detected a corruption in the data on unpacking. |
82 scoped_ptr<EntryKernel> UnpackEntry(sql::Statement* statement) { | 88 scoped_ptr<EntryKernel> UnpackEntry(sql::Statement* statement) { |
83 scoped_ptr<EntryKernel> kernel(new EntryKernel()); | 89 scoped_ptr<EntryKernel> kernel(new EntryKernel()); |
84 DCHECK_EQ(statement->ColumnCount(), static_cast<int>(FIELD_COUNT)); | 90 DCHECK_EQ(statement->ColumnCount(), static_cast<int>(FIELD_COUNT)); |
(...skipping 13 matching lines...) Expand all Loading... |
98 kernel->put(static_cast<BitField>(i), (0 != statement->ColumnInt(i))); | 104 kernel->put(static_cast<BitField>(i), (0 != statement->ColumnInt(i))); |
99 } | 105 } |
100 for ( ; i < STRING_FIELDS_END; ++i) { | 106 for ( ; i < STRING_FIELDS_END; ++i) { |
101 kernel->put(static_cast<StringField>(i), | 107 kernel->put(static_cast<StringField>(i), |
102 statement->ColumnString(i)); | 108 statement->ColumnString(i)); |
103 } | 109 } |
104 for ( ; i < PROTO_FIELDS_END; ++i) { | 110 for ( ; i < PROTO_FIELDS_END; ++i) { |
105 kernel->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray( | 111 kernel->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray( |
106 statement->ColumnBlob(i), statement->ColumnByteLength(i)); | 112 statement->ColumnBlob(i), statement->ColumnByteLength(i)); |
107 } | 113 } |
108 for( ; i < ORDINAL_FIELDS_END; ++i) { | 114 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) { |
109 std::string temp; | 115 std::string temp; |
110 statement->ColumnBlobAsString(i, &temp); | 116 statement->ColumnBlobAsString(i, &temp); |
111 NodeOrdinal unpacked_ord(temp); | |
112 | 117 |
113 // Its safe to assume that an invalid ordinal is a sign that | 118 // An empty value indicates an intentionally invalid position. |
114 // some external corruption has occurred. Return NULL to force | 119 // If the string is non-empty, though, the position should be valid. |
115 // a re-download of the sync data. | 120 if (!temp.empty()) { |
116 if(!unpacked_ord.IsValid()) { | 121 UniquePosition unpacked_pos = UniquePosition::FromBytes(temp); |
117 DVLOG(1) << "Unpacked invalid ordinal. Signaling that the DB is corrupt"; | 122 if(!unpacked_pos.IsValid()) { |
118 return scoped_ptr<EntryKernel>(NULL); | 123 DVLOG(1) << "Unpacked invalid position. Assuming the DB is corrupt"; |
| 124 return scoped_ptr<EntryKernel>(NULL); |
| 125 } |
| 126 kernel->mutable_ref(static_cast<UniquePositionField>(i)) = unpacked_pos; |
119 } | 127 } |
120 kernel->mutable_ref(static_cast<OrdinalField>(i)) = unpacked_ord; | 128 } |
| 129 for ( ; i < BYTES_FIELDS_END; ++i) { |
| 130 std::string temp; |
| 131 statement->ColumnBlobAsString(i, &temp); |
| 132 kernel->put(static_cast<BytesField>(i), temp); |
121 } | 133 } |
122 return kernel.Pass(); | 134 return kernel.Pass(); |
123 } | 135 } |
124 | 136 |
125 namespace { | 137 namespace { |
126 | 138 |
127 string ComposeCreateTableColumnSpecs() { | 139 string ComposeCreateTableColumnSpecs() { |
128 const ColumnSpec* begin = g_metas_columns; | 140 const ColumnSpec* begin = g_metas_columns; |
129 const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns); | 141 const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns); |
130 string query; | 142 string query; |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 if (MigrateVersion83To84()) | 382 if (MigrateVersion83To84()) |
371 version_on_disk = 84; | 383 version_on_disk = 84; |
372 } | 384 } |
373 | 385 |
374 // Version 85 migration removes the initial_sync_ended bits. | 386 // Version 85 migration removes the initial_sync_ended bits. |
375 if (version_on_disk == 84) { | 387 if (version_on_disk == 84) { |
376 if (MigrateVersion84To85()) | 388 if (MigrateVersion84To85()) |
377 version_on_disk = 85; | 389 version_on_disk = 85; |
378 } | 390 } |
379 | 391 |
| 392 // Version 86 migration converts bookmarks to the unique positioning system. |
| 393 // It also introduces a new field to store a unique ID for each bookmark. |
| 394 if (version_on_disk == 85) { |
| 395 if (MigrateVersion85To86()) |
| 396 version_on_disk = 86; |
| 397 } |
| 398 |
380 // If one of the migrations requested it, drop columns that aren't current. | 399 // If one of the migrations requested it, drop columns that aren't current. |
381 // It's only safe to do this after migrating all the way to the current | 400 // It's only safe to do this after migrating all the way to the current |
382 // version. | 401 // version. |
383 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { | 402 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { |
384 if (!RefreshColumns()) | 403 if (!RefreshColumns()) |
385 version_on_disk = 0; | 404 version_on_disk = 0; |
386 } | 405 } |
387 | 406 |
388 // A final, alternative catch-all migration to simply re-sync everything. | 407 // A final, alternative catch-all migration to simply re-sync everything. |
389 if (version_on_disk != kCurrentDBVersion) { | 408 if (version_on_disk != kCurrentDBVersion) { |
(...skipping 578 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
968 needs_column_refresh_ = true; | 987 needs_column_refresh_ = true; |
969 return true; | 988 return true; |
970 } | 989 } |
971 | 990 |
972 bool DirectoryBackingStore::MigrateVersion76To77() { | 991 bool DirectoryBackingStore::MigrateVersion76To77() { |
973 // This change changes the format of stored timestamps to ms since | 992 // This change changes the format of stored timestamps to ms since |
974 // the Unix epoch. | 993 // the Unix epoch. |
975 #if defined(OS_WIN) | 994 #if defined(OS_WIN) |
976 // On Windows, we used to store timestamps in FILETIME format (100s of | 995 // On Windows, we used to store timestamps in FILETIME format (100s of |
977 // ns since Jan 1, 1601). Magic numbers taken from | 996 // ns since Jan 1, 1601). Magic numbers taken from |
978 // http://stackoverflow.com/questions/5398557/java-library-for-dealing-with-win3
2-filetime | 997 // http://stackoverflow.com/questions/5398557/ |
| 998 // java-library-for-dealing-with-win32-filetime |
979 // . | 999 // . |
980 #define TO_UNIX_TIME_MS(x) #x " = " #x " / 10000 - 11644473600000" | 1000 #define TO_UNIX_TIME_MS(x) #x " = " #x " / 10000 - 11644473600000" |
981 #else | 1001 #else |
982 // On other platforms, we used to store timestamps in time_t format (s | 1002 // On other platforms, we used to store timestamps in time_t format (s |
983 // since the Unix epoch). | 1003 // since the Unix epoch). |
984 #define TO_UNIX_TIME_MS(x) #x " = " #x " * 1000" | 1004 #define TO_UNIX_TIME_MS(x) #x " = " #x " * 1000" |
985 #endif | 1005 #endif |
986 sql::Statement update_timestamps(db_->GetUniqueStatement( | 1006 sql::Statement update_timestamps(db_->GetUniqueStatement( |
987 "UPDATE metas SET " | 1007 "UPDATE metas SET " |
988 TO_UNIX_TIME_MS(mtime) ", " | 1008 TO_UNIX_TIME_MS(mtime) ", " |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1093 string query = "CREATE TABLE deleted_metas "; | 1113 string query = "CREATE TABLE deleted_metas "; |
1094 query.append(ComposeCreateTableColumnSpecs()); | 1114 query.append(ComposeCreateTableColumnSpecs()); |
1095 if (!db_->Execute(query.c_str())) | 1115 if (!db_->Execute(query.c_str())) |
1096 return false; | 1116 return false; |
1097 | 1117 |
1098 SetVersion(84); | 1118 SetVersion(84); |
1099 return true; | 1119 return true; |
1100 } | 1120 } |
1101 | 1121 |
1102 bool DirectoryBackingStore::MigrateVersion84To85() { | 1122 bool DirectoryBackingStore::MigrateVersion84To85() { |
1103 // Version 84 removes the initial_sync_ended flag. | 1123 // Version 85 removes the initial_sync_ended flag. |
1104 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) | 1124 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) |
1105 return false; | 1125 return false; |
1106 if (!CreateModelsTable()) | 1126 if (!CreateModelsTable()) |
1107 return false; | 1127 return false; |
1108 if (!db_->Execute("INSERT INTO models SELECT " | 1128 if (!db_->Execute("INSERT INTO models SELECT " |
1109 "model_id, progress_marker, transaction_version " | 1129 "model_id, progress_marker, transaction_version " |
1110 "FROM temp_models")) { | 1130 "FROM temp_models")) { |
1111 return false; | 1131 return false; |
1112 } | 1132 } |
1113 SafeDropTable("temp_models"); | 1133 SafeDropTable("temp_models"); |
1114 | 1134 |
1115 SetVersion(85); | 1135 SetVersion(85); |
1116 return true; | 1136 return true; |
1117 } | 1137 } |
1118 | 1138 |
| 1139 bool DirectoryBackingStore::MigrateVersion85To86() { |
| 1140 // Version 86 removes both server ordinals and local NEXT_ID, PREV_ID and |
| 1141 // SERVER_{POSITION,ORDINAL}_IN_PARENT and replaces them with UNIQUE_POSITION |
| 1142 // and SERVER_UNIQUE_POSITION. |
| 1143 if (!db_->Execute("ALTER TABLE metas ADD COLUMN " |
| 1144 "server_unique_position BLOB")) { |
| 1145 return false; |
| 1146 } |
| 1147 if (!db_->Execute("ALTER TABLE metas ADD COLUMN " |
| 1148 "unique_position BLOB")) { |
| 1149 return false; |
| 1150 } |
| 1151 if (!db_->Execute("ALTER TABLE metas ADD COLUMN " |
| 1152 "unique_bookmark_tag BLOB")) { |
| 1153 return false; |
| 1154 } |
| 1155 |
| 1156 sql::Statement get(db_->GetUniqueStatement( |
| 1157 "SELECT metahandle, id, specifics, server_ordinal_in_parent FROM metas")); |
| 1158 |
| 1159 // Note that we set both the local and server position based on the server |
| 1160 // position. We wll lose any unsynced local position changes. Unfortunately, |
| 1161 // there's nothing we can do to avoid that. The NEXT_ID / PREV_ID values |
| 1162 // can't be translated into a UNIQUE_POSTION in a reliable way. |
| 1163 sql::Statement put(db_->GetCachedStatement( |
| 1164 SQL_FROM_HERE, |
| 1165 "UPDATE metas SET" |
| 1166 " server_unique_position = ?," |
| 1167 " unique_position = ?," |
| 1168 " unique_bookmark_tag = ?" |
| 1169 "WHERE metahandle = ?")); |
| 1170 |
| 1171 while (get.Step()) { |
| 1172 int64 metahandle = get.ColumnInt64(0); |
| 1173 |
| 1174 sync_pb::EntitySpecifics specifics; |
| 1175 specifics.ParseFromArray( |
| 1176 get.ColumnBlob(1), get.ColumnByteLength(1)); |
| 1177 |
| 1178 std::string id_string; |
| 1179 get.ColumnBlobAsString(2, &id_string); |
| 1180 |
| 1181 std::string ordinal_string; |
| 1182 get.ColumnBlobAsString(3, &ordinal_string); |
| 1183 NodeOrdinal ordinal(ordinal_string); |
| 1184 |
| 1185 std::string unique_bookmark_tag; |
| 1186 |
| 1187 UniquePosition position; |
| 1188 if (ShouldMaintainPosition(GetModelTypeFromSpecifics(specifics))) { |
| 1189 // Ideally, the tag would be based on the originator_cache_guid and |
| 1190 // the originator_item_id. That's something that all clients can |
| 1191 // agree on. |
| 1192 // |
| 1193 // Unfortunately, that information isn't available here. We will base our |
| 1194 // tag on the ID instead, which is slightly less consistent. For fully |
| 1195 // synced items, the tags will match, but there could be some mismatches |
| 1196 // if there happen to be client IDs during the migration. |
| 1197 // |
| 1198 // To get everyone back into a synced state, we will update the bookmark |
| 1199 // tag according to the originator_cache_guid and originator_item_id when |
| 1200 // we see updates for this item. That should ensure that commonly |
| 1201 // modified items will end up with the proper tag values eventually. |
| 1202 std::string unique_bookmark_tag; |
| 1203 |
| 1204 // FIXME: Include some sort of tag prefix here to avoid "collisions"? |
| 1205 std::string hash = base::SHA1HashString(id_string); |
| 1206 |
| 1207 CHECK(base::Base64Encode(hash, &unique_bookmark_tag)); |
| 1208 |
| 1209 int64 int_position = NodeOrdinalToInt64(ordinal); |
| 1210 position = UniquePosition::FromInt64(int_position, unique_bookmark_tag); |
| 1211 } else { |
| 1212 // Leave bookmark_tag and position at their default (invalid) values. |
| 1213 } |
| 1214 |
| 1215 const std::string position_blob = position.ToInternalValue(); |
| 1216 put.BindBlob(0, position_blob.data(), position_blob.length()); |
| 1217 put.BindBlob(1, position_blob.data(), position_blob.length()); |
| 1218 put.BindBlob(2, unique_bookmark_tag.data(), unique_bookmark_tag.length()); |
| 1219 put.BindInt64(3, metahandle); |
| 1220 |
| 1221 if (!put.Run()) |
| 1222 return false; |
| 1223 put.Reset(true); |
| 1224 } |
| 1225 |
| 1226 SetVersion(86); |
| 1227 needs_column_refresh_ = true; |
| 1228 return true; |
| 1229 } |
| 1230 |
1119 bool DirectoryBackingStore::CreateTables() { | 1231 bool DirectoryBackingStore::CreateTables() { |
1120 DVLOG(1) << "First run, creating tables"; | 1232 DVLOG(1) << "First run, creating tables"; |
1121 // Create two little tables share_version and share_info | 1233 // Create two little tables share_version and share_info |
1122 if (!db_->Execute( | 1234 if (!db_->Execute( |
1123 "CREATE TABLE share_version (" | 1235 "CREATE TABLE share_version (" |
1124 "id VARCHAR(128) primary key, data INT)")) { | 1236 "id VARCHAR(128) primary key, data INT)")) { |
1125 return false; | 1237 return false; |
1126 } | 1238 } |
1127 | 1239 |
1128 { | 1240 { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1171 | 1283 |
1172 // Create the big metas table. | 1284 // Create the big metas table. |
1173 if (!CreateMetasTable(false)) | 1285 if (!CreateMetasTable(false)) |
1174 return false; | 1286 return false; |
1175 | 1287 |
1176 { | 1288 { |
1177 // Insert the entry for the root into the metas table. | 1289 // Insert the entry for the root into the metas table. |
1178 const int64 now = TimeToProtoTime(base::Time::Now()); | 1290 const int64 now = TimeToProtoTime(base::Time::Now()); |
1179 sql::Statement s(db_->GetUniqueStatement( | 1291 sql::Statement s(db_->GetUniqueStatement( |
1180 "INSERT INTO metas " | 1292 "INSERT INTO metas " |
1181 "( id, metahandle, is_dir, ctime, mtime, server_ordinal_in_parent) " | 1293 "( id, metahandle, is_dir, ctime, mtime ) " |
1182 "VALUES ( \"r\", 1, 1, ?, ?, ?)")); | 1294 "VALUES ( \"r\", 1, 1, ?, ? )")); |
1183 s.BindInt64(0, now); | 1295 s.BindInt64(0, now); |
1184 s.BindInt64(1, now); | 1296 s.BindInt64(1, now); |
1185 const std::string ord = | |
1186 NodeOrdinal::CreateInitialOrdinal().ToInternalValue(); | |
1187 s.BindBlob(2, ord.data(), ord.length()); | |
1188 | 1297 |
1189 if (!s.Run()) | 1298 if (!s.Run()) |
1190 return false; | 1299 return false; |
1191 } | 1300 } |
1192 | 1301 |
1193 return true; | 1302 return true; |
1194 } | 1303 } |
1195 | 1304 |
1196 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) { | 1305 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) { |
1197 string query = "CREATE TABLE "; | 1306 string query = "CREATE TABLE "; |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1300 it != index.end(); ++it) { | 1409 it != index.end(); ++it) { |
1301 EntryKernel* entry = *it; | 1410 EntryKernel* entry = *it; |
1302 bool is_duplicate_id = !(ids_set.insert(entry->ref(ID).value()).second); | 1411 bool is_duplicate_id = !(ids_set.insert(entry->ref(ID).value()).second); |
1303 is_ok = is_ok && !is_duplicate_id; | 1412 is_ok = is_ok && !is_duplicate_id; |
1304 } | 1413 } |
1305 | 1414 |
1306 IdsSet::iterator end = ids_set.end(); | 1415 IdsSet::iterator end = ids_set.end(); |
1307 for (MetahandlesIndex::const_iterator it = index.begin(); | 1416 for (MetahandlesIndex::const_iterator it = index.begin(); |
1308 it != index.end(); ++it) { | 1417 it != index.end(); ++it) { |
1309 EntryKernel* entry = *it; | 1418 EntryKernel* entry = *it; |
1310 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end); | |
1311 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); | 1419 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); |
1312 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); | 1420 if (!parent_exists) { |
1313 is_ok = is_ok && prev_exists && parent_exists && next_exists; | 1421 return false; |
| 1422 } |
1314 } | 1423 } |
1315 return is_ok; | 1424 return is_ok; |
1316 } | 1425 } |
1317 | 1426 |
1318 } // namespace syncable | 1427 } // namespace syncable |
1319 } // namespace syncer | 1428 } // namespace syncer |
OLD | NEW |