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

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

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months 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
OLDNEW
(Empty)
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "sync/syncable/directory_backing_store.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <limits>
11 #include <unordered_set>
12
13 #include "base/base64.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/rand_util.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "base/time/time.h"
23 #include "base/trace_event/trace_event.h"
24 #include "build/build_config.h"
25 #include "sql/connection.h"
26 #include "sql/error_delegate_util.h"
27 #include "sql/statement.h"
28 #include "sql/transaction.h"
29 #include "sync/internal_api/public/base/node_ordinal.h"
30 #include "sync/protocol/bookmark_specifics.pb.h"
31 #include "sync/protocol/sync.pb.h"
32 #include "sync/syncable/syncable-inl.h"
33 #include "sync/syncable/syncable_columns.h"
34 #include "sync/syncable/syncable_util.h"
35 #include "sync/util/time.h"
36
37 using std::string;
38
39 namespace syncer {
40 namespace syncable {
41
42 // Increment this version whenever updating DB tables.
43 const int32_t kCurrentDBVersion = 90;
44
45 // The current database page size in Kilobytes.
46 const int32_t kCurrentPageSizeKB = 32768;
47
48 // Iterate over the fields of |entry| and bind each to |statement| for
49 // updating. Returns the number of args bound.
50 void BindFields(const EntryKernel& entry,
51 sql::Statement* statement) {
52 int index = 0;
53 int i = 0;
54 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
55 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i)));
56 }
57 for ( ; i < TIME_FIELDS_END; ++i) {
58 statement->BindInt64(index++,
59 TimeToProtoTime(
60 entry.ref(static_cast<TimeField>(i))));
61 }
62 for ( ; i < ID_FIELDS_END; ++i) {
63 statement->BindString(index++, entry.ref(static_cast<IdField>(i)).s_);
64 }
65 for ( ; i < BIT_FIELDS_END; ++i) {
66 statement->BindInt(index++, entry.ref(static_cast<BitField>(i)));
67 }
68 for ( ; i < STRING_FIELDS_END; ++i) {
69 statement->BindString(index++, entry.ref(static_cast<StringField>(i)));
70 }
71 for ( ; i < PROTO_FIELDS_END; ++i) {
72 std::string temp;
73 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp);
74 statement->BindBlob(index++, temp.data(), temp.length());
75 }
76 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) {
77 std::string temp;
78 entry.ref(static_cast<UniquePositionField>(i)).SerializeToString(&temp);
79 statement->BindBlob(index++, temp.data(), temp.length());
80 }
81 for (; i < ATTACHMENT_METADATA_FIELDS_END; ++i) {
82 std::string temp;
83 entry.ref(static_cast<AttachmentMetadataField>(i)).SerializeToString(&temp);
84 statement->BindBlob(index++, temp.data(), temp.length());
85 }
86 }
87
88 // Helper function that loads a number of shareable fields of the
89 // same type. The sharing criteria is based on comparison of
90 // the serialized data. Only consecutive DB columns need to compared
91 // to cover all possible sharing combinations.
92 template <typename TValue, typename TField>
93 void UnpackProtoFields(sql::Statement* statement,
94 EntryKernel* kernel,
95 int* index,
96 int end_index,
97 int* total_entry_copies) {
98 const void* prev_blob = nullptr;
99 int prev_length = -1;
100 int prev_index = -1;
101
102 for (; *index < end_index; ++(*index)) {
103 int length = statement->ColumnByteLength(*index);
104 if (length == 0) {
105 // Skip this column and keep the default value in the kernel field.
106 continue;
107 }
108
109 const void* blob = statement->ColumnBlob(*index);
110 // According to sqlite3 documentation, the prev_blob pointer should remain
111 // valid until moving to the next row.
112 if (length == prev_length && memcmp(blob, prev_blob, length) == 0) {
113 // Serialized values are the same - share the value from |prev_index|
114 // field with the current field.
115 kernel->copy(static_cast<TField>(prev_index),
116 static_cast<TField>(*index));
117 } else {
118 // Regular case - deserialize and copy the value to the field.
119 kernel->load(static_cast<TField>(*index), blob, length);
120 prev_blob = blob;
121 prev_length = length;
122 prev_index = *index;
123 ++(*total_entry_copies);
124 }
125 }
126 }
127
128 // The caller owns the returned EntryKernel*. Assumes the statement currently
129 // points to a valid row in the metas table. Returns NULL to indicate that
130 // it detected a corruption in the data on unpacking.
131 std::unique_ptr<EntryKernel> UnpackEntry(sql::Statement* statement,
132 int* total_specifics_copies) {
133 std::unique_ptr<EntryKernel> kernel(new EntryKernel());
134 DCHECK_EQ(statement->ColumnCount(), static_cast<int>(FIELD_COUNT));
135 int i = 0;
136 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
137 kernel->put(static_cast<Int64Field>(i), statement->ColumnInt64(i));
138 }
139 for ( ; i < TIME_FIELDS_END; ++i) {
140 kernel->put(static_cast<TimeField>(i),
141 ProtoTimeToTime(statement->ColumnInt64(i)));
142 }
143 for ( ; i < ID_FIELDS_END; ++i) {
144 kernel->mutable_ref(static_cast<IdField>(i)).s_ =
145 statement->ColumnString(i);
146 }
147 for ( ; i < BIT_FIELDS_END; ++i) {
148 kernel->put(static_cast<BitField>(i), (0 != statement->ColumnInt(i)));
149 }
150 for ( ; i < STRING_FIELDS_END; ++i) {
151 kernel->put(static_cast<StringField>(i),
152 statement->ColumnString(i));
153 }
154 UnpackProtoFields<sync_pb::EntitySpecifics, ProtoField>(
155 statement, kernel.get(), &i, PROTO_FIELDS_END, total_specifics_copies);
156 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) {
157 std::string temp;
158 statement->ColumnBlobAsString(i, &temp);
159
160 sync_pb::UniquePosition proto;
161 if (!proto.ParseFromString(temp)) {
162 DVLOG(1) << "Unpacked invalid position. Assuming the DB is corrupt";
163 return std::unique_ptr<EntryKernel>();
164 }
165
166 kernel->mutable_ref(static_cast<UniquePositionField>(i)) =
167 UniquePosition::FromProto(proto);
168 }
169 int attachemnt_specifics_counts = 0;
170 UnpackProtoFields<sync_pb::AttachmentMetadata, AttachmentMetadataField>(
171 statement, kernel.get(), &i, ATTACHMENT_METADATA_FIELDS_END,
172 &attachemnt_specifics_counts);
173
174 // Sanity check on positions. We risk strange and rare crashes if our
175 // assumptions about unique position values are broken.
176 if (kernel->ShouldMaintainPosition() &&
177 !kernel->ref(UNIQUE_POSITION).IsValid()) {
178 DVLOG(1) << "Unpacked invalid position on an entity that should have a "
179 << "valid position. Assuming the DB is corrupt.";
180 return std::unique_ptr<EntryKernel>();
181 }
182
183 return kernel;
184 }
185
186 namespace {
187
188 // This just has to be big enough to hold an UPDATE or INSERT statement that
189 // modifies all the columns in the entry table.
190 static const string::size_type kUpdateStatementBufferSize = 2048;
191
192 void OnSqliteError(const base::Closure& catastrophic_error_handler,
193 int err,
194 sql::Statement* statement) {
195 // An error has been detected. Ignore unless it is catastrophic.
196 if (sql::IsErrorCatastrophic(err)) {
197 // At this point sql::* and DirectoryBackingStore may be on the callstack so
198 // don't invoke the error handler directly. Instead, PostTask to this thread
199 // to avoid potential reentrancy issues.
200 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
201 catastrophic_error_handler);
202 }
203 }
204
205 string ComposeCreateTableColumnSpecs() {
206 const ColumnSpec* begin = g_metas_columns;
207 const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns);
208 string query;
209 query.reserve(kUpdateStatementBufferSize);
210 char separator = '(';
211 for (const ColumnSpec* column = begin; column != end; ++column) {
212 query.push_back(separator);
213 separator = ',';
214 query.append(column->name);
215 query.push_back(' ');
216 query.append(column->spec);
217 }
218 query.push_back(')');
219 return query;
220 }
221
222 void AppendColumnList(std::string* output) {
223 const char* joiner = " ";
224 // Be explicit in SELECT order to match up with UnpackEntry.
225 for (int i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) {
226 output->append(joiner);
227 output->append(ColumnName(i));
228 joiner = ", ";
229 }
230 }
231
232 bool SaveEntryToDB(sql::Statement* save_statement, const EntryKernel& entry) {
233 save_statement->Reset(true);
234 BindFields(entry, save_statement);
235 return save_statement->Run();
236 }
237
238 // total_specifics_copies : Total copies of entries in memory, include extra
239 // copy for some entries which create by copy-on-write mechanism.
240 // entries_counts : entry counts for each model type.
241 void UploadModelTypeEntryCount(const int total_specifics_copies,
242 const int(&entries_counts)[MODEL_TYPE_COUNT]) {
243 int total_entry_counts = 0;
244 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
245 std::string model_type;
246 if (RealModelTypeToNotificationType((ModelType)i, &model_type)) {
247 std::string full_histogram_name = "Sync.ModelTypeCount." + model_type;
248 base::HistogramBase* histogram = base::Histogram::FactoryGet(
249 full_histogram_name, 1, 1000000, 50,
250 base::HistogramBase::kUmaTargetedHistogramFlag);
251 if (histogram)
252 histogram->Add(entries_counts[i]);
253 total_entry_counts += entries_counts[i];
254 }
255 }
256 UMA_HISTOGRAM_COUNTS("Sync.ModelTypeCount", total_entry_counts);
257 UMA_HISTOGRAM_COUNTS("Sync.ExtraSyncDataCount",
258 total_specifics_copies - total_entry_counts);
259 }
260
261 } // namespace
262
263 ///////////////////////////////////////////////////////////////////////////////
264 // DirectoryBackingStore implementation.
265
266 DirectoryBackingStore::DirectoryBackingStore(const string& dir_name)
267 : dir_name_(dir_name),
268 database_page_size_(kCurrentPageSizeKB),
269 needs_metas_column_refresh_(false),
270 needs_share_info_column_refresh_(false) {
271 DCHECK(base::ThreadTaskRunnerHandle::IsSet());
272 ResetAndCreateConnection();
273 }
274
275 DirectoryBackingStore::DirectoryBackingStore(const string& dir_name,
276 sql::Connection* db)
277 : dir_name_(dir_name),
278 database_page_size_(kCurrentPageSizeKB),
279 db_(db),
280 needs_metas_column_refresh_(false),
281 needs_share_info_column_refresh_(false) {
282 DCHECK(base::ThreadTaskRunnerHandle::IsSet());
283 }
284
285 DirectoryBackingStore::~DirectoryBackingStore() {
286 }
287
288 bool DirectoryBackingStore::DeleteEntries(EntryTable from,
289 const MetahandleSet& handles) {
290 if (handles.empty())
291 return true;
292
293 sql::Statement statement;
294 // Call GetCachedStatement() separately to get different statements for
295 // different tables.
296 switch (from) {
297 case METAS_TABLE:
298 statement.Assign(db_->GetCachedStatement(
299 SQL_FROM_HERE, "DELETE FROM metas WHERE metahandle = ?"));
300 break;
301 case DELETE_JOURNAL_TABLE:
302 statement.Assign(db_->GetCachedStatement(
303 SQL_FROM_HERE, "DELETE FROM deleted_metas WHERE metahandle = ?"));
304 break;
305 }
306
307 for (MetahandleSet::const_iterator i = handles.begin(); i != handles.end();
308 ++i) {
309 statement.BindInt64(0, *i);
310 if (!statement.Run())
311 return false;
312 statement.Reset(true);
313 }
314 return true;
315 }
316
317 bool DirectoryBackingStore::SaveChanges(
318 const Directory::SaveChangesSnapshot& snapshot) {
319 DCHECK(CalledOnValidThread());
320 DCHECK(db_->is_open());
321
322 // Back out early if there is nothing to write.
323 bool save_info =
324 (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status);
325 if (!snapshot.HasUnsavedMetahandleChanges() && !save_info) {
326 return true;
327 }
328
329 sql::Transaction transaction(db_.get());
330 if (!transaction.Begin())
331 return false;
332
333 PrepareSaveEntryStatement(METAS_TABLE, &save_meta_statement_);
334 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
335 i != snapshot.dirty_metas.end(); ++i) {
336 DCHECK((*i)->is_dirty());
337 if (!SaveEntryToDB(&save_meta_statement_, **i))
338 return false;
339 }
340
341 if (!DeleteEntries(METAS_TABLE, snapshot.metahandles_to_purge))
342 return false;
343
344 PrepareSaveEntryStatement(DELETE_JOURNAL_TABLE,
345 &save_delete_journal_statement_);
346 for (EntryKernelSet::const_iterator i = snapshot.delete_journals.begin();
347 i != snapshot.delete_journals.end(); ++i) {
348 if (!SaveEntryToDB(&save_delete_journal_statement_, **i))
349 return false;
350 }
351
352 if (!DeleteEntries(DELETE_JOURNAL_TABLE, snapshot.delete_journals_to_purge))
353 return false;
354
355 if (save_info) {
356 const Directory::PersistedKernelInfo& info = snapshot.kernel_info;
357 sql::Statement s1(db_->GetCachedStatement(
358 SQL_FROM_HERE,
359 "UPDATE share_info "
360 "SET store_birthday = ?, "
361 "bag_of_chips = ?"));
362 s1.BindString(0, info.store_birthday);
363 s1.BindBlob(1, info.bag_of_chips.data(), info.bag_of_chips.size());
364
365 if (!s1.Run())
366 return false;
367 DCHECK_EQ(db_->GetLastChangeCount(), 1);
368
369 sql::Statement s2(db_->GetCachedStatement(
370 SQL_FROM_HERE,
371 "INSERT OR REPLACE "
372 "INTO models (model_id, "
373 "progress_marker, "
374 "transaction_version, "
375 "context) "
376 "VALUES (?, ?, ?, ?)"));
377
378 ModelTypeSet protocol_types = ProtocolTypes();
379 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
380 iter.Inc()) {
381 ModelType type = iter.Get();
382 // We persist not ModelType but rather a protobuf-derived ID.
383 string model_id = ModelTypeEnumToModelId(type);
384 string progress_marker;
385 info.download_progress[type].SerializeToString(&progress_marker);
386 s2.BindBlob(0, model_id.data(), model_id.length());
387 s2.BindBlob(1, progress_marker.data(), progress_marker.length());
388 s2.BindInt64(2, info.transaction_version[type]);
389 string context;
390 info.datatype_context[type].SerializeToString(&context);
391 s2.BindBlob(3, context.data(), context.length());
392 if (!s2.Run())
393 return false;
394 DCHECK_EQ(db_->GetLastChangeCount(), 1);
395 s2.Reset(true);
396 }
397 }
398
399 return transaction.Commit();
400 }
401
402 sql::Connection* DirectoryBackingStore::db() {
403 return db_.get();
404 }
405
406 bool DirectoryBackingStore::IsOpen() const {
407 return db_->is_open();
408 }
409
410 bool DirectoryBackingStore::Open(const base::FilePath& path) {
411 DCHECK(!db_->is_open());
412 return db_->Open(path);
413 }
414
415 bool DirectoryBackingStore::OpenInMemory() {
416 DCHECK(!db_->is_open());
417 return db_->OpenInMemory();
418 }
419
420 bool DirectoryBackingStore::InitializeTables() {
421 if (!UpdatePageSizeIfNecessary())
422 return false;
423
424 sql::Transaction transaction(db_.get());
425 if (!transaction.Begin())
426 return false;
427
428 if (!db_->DoesTableExist("share_version")) {
429 // Delete the existing database (if any), and create a fresh one.
430 DropAllTables();
431 if (!CreateTables())
432 return false;
433 }
434
435 int version_on_disk = GetVersion();
436
437 // Upgrade from version 67. Version 67 was widely distributed as the original
438 // Bookmark Sync release. Version 68 removed unique naming.
439 if (version_on_disk == 67) {
440 if (MigrateVersion67To68())
441 version_on_disk = 68;
442 }
443 // Version 69 introduced additional datatypes.
444 if (version_on_disk == 68) {
445 if (MigrateVersion68To69())
446 version_on_disk = 69;
447 }
448
449 if (version_on_disk == 69) {
450 if (MigrateVersion69To70())
451 version_on_disk = 70;
452 }
453
454 // Version 71 changed the sync progress information to be per-datatype.
455 if (version_on_disk == 70) {
456 if (MigrateVersion70To71())
457 version_on_disk = 71;
458 }
459
460 // Version 72 removed extended attributes, a legacy way to do extensible
461 // key/value information, stored in their own table.
462 if (version_on_disk == 71) {
463 if (MigrateVersion71To72())
464 version_on_disk = 72;
465 }
466
467 // Version 73 added a field for notification state.
468 if (version_on_disk == 72) {
469 if (MigrateVersion72To73())
470 version_on_disk = 73;
471 }
472
473 // Version 74 added state for the autofill migration.
474 if (version_on_disk == 73) {
475 if (MigrateVersion73To74())
476 version_on_disk = 74;
477 }
478
479 // Version 75 migrated from int64_t-based timestamps to per-datatype tokens.
480 if (version_on_disk == 74) {
481 if (MigrateVersion74To75())
482 version_on_disk = 75;
483 }
484
485 // Version 76 removed all (5) autofill migration related columns.
486 if (version_on_disk == 75) {
487 if (MigrateVersion75To76())
488 version_on_disk = 76;
489 }
490
491 // Version 77 standardized all time fields to ms since the Unix
492 // epoch.
493 if (version_on_disk == 76) {
494 if (MigrateVersion76To77())
495 version_on_disk = 77;
496 }
497
498 // Version 78 added the column base_server_specifics to the metas table.
499 if (version_on_disk == 77) {
500 if (MigrateVersion77To78())
501 version_on_disk = 78;
502 }
503
504 // Version 79 migration is a one-time fix for some users in a bad state.
505 if (version_on_disk == 78) {
506 if (MigrateVersion78To79())
507 version_on_disk = 79;
508 }
509
510 // Version 80 migration is adding the bag_of_chips column.
511 if (version_on_disk == 79) {
512 if (MigrateVersion79To80())
513 version_on_disk = 80;
514 }
515
516 // Version 81 replaces the int64_t server_position_in_parent_field
517 // with a blob server_ordinal_in_parent field.
518 if (version_on_disk == 80) {
519 if (MigrateVersion80To81())
520 version_on_disk = 81;
521 }
522
523 // Version 82 migration added transaction_version column per data type.
524 if (version_on_disk == 81) {
525 if (MigrateVersion81To82())
526 version_on_disk = 82;
527 }
528
529 // Version 83 migration added transaction_version column per sync entry.
530 if (version_on_disk == 82) {
531 if (MigrateVersion82To83())
532 version_on_disk = 83;
533 }
534
535 // Version 84 migration added deleted_metas table.
536 if (version_on_disk == 83) {
537 if (MigrateVersion83To84())
538 version_on_disk = 84;
539 }
540
541 // Version 85 migration removes the initial_sync_ended bits.
542 if (version_on_disk == 84) {
543 if (MigrateVersion84To85())
544 version_on_disk = 85;
545 }
546
547 // Version 86 migration converts bookmarks to the unique positioning system.
548 // It also introduces a new field to store a unique ID for each bookmark.
549 if (version_on_disk == 85) {
550 if (MigrateVersion85To86())
551 version_on_disk = 86;
552 }
553
554 // Version 87 migration adds a collection of attachment ids per sync entry.
555 if (version_on_disk == 86) {
556 if (MigrateVersion86To87())
557 version_on_disk = 87;
558 }
559
560 // Version 88 migration adds datatype contexts to the models table.
561 if (version_on_disk == 87) {
562 if (MigrateVersion87To88())
563 version_on_disk = 88;
564 }
565
566 // Version 89 migration adds server attachment metadata to the metas table.
567 if (version_on_disk == 88) {
568 if (MigrateVersion88To89())
569 version_on_disk = 89;
570 }
571
572 // Version 90 migration removes several columns from share_info table.
573 if (version_on_disk == 89) {
574 if (MigrateVersion89To90())
575 version_on_disk = 90;
576 }
577
578 // If one of the migrations requested it, drop columns that aren't current.
579 // It's only safe to do this after migrating all the way to the current
580 // version.
581 if (version_on_disk == kCurrentDBVersion && needs_column_refresh()) {
582 if (!RefreshColumns())
583 return false;
584 }
585
586 // In case of error, let the caller decide whether to re-sync from scratch
587 // with a new database.
588 if (version_on_disk != kCurrentDBVersion)
589 return false;
590
591 return transaction.Commit();
592 }
593
594 // This function drops unused columns by creating a new table that contains only
595 // the currently used columns then copying all rows from the old tables into
596 // this new one. The tables are then rearranged so the new replaces the old.
597 bool DirectoryBackingStore::RefreshColumns() {
598 DCHECK(needs_metas_column_refresh_ || needs_share_info_column_refresh_);
599
600 if (needs_metas_column_refresh_) {
601 // Create a new table named temp_metas.
602 SafeDropTable("temp_metas");
603 if (!CreateMetasTable(true))
604 return false;
605
606 // Populate temp_metas from metas.
607 //
608 // At this point, the metas table may contain columns belonging to obsolete
609 // schema versions. This statement explicitly lists only the columns that
610 // belong to the current schema version, so the obsolete columns will be
611 // effectively dropped once we rename temp_metas over top of metas.
612 std::string query = "INSERT INTO temp_metas (";
613 AppendColumnList(&query);
614 query.append(") SELECT ");
615 AppendColumnList(&query);
616 query.append(" FROM metas");
617 if (!db_->Execute(query.c_str()))
618 return false;
619
620 // Drop metas.
621 SafeDropTable("metas");
622
623 // Rename temp_metas -> metas.
624 if (!db_->Execute("ALTER TABLE temp_metas RENAME TO metas"))
625 return false;
626
627 needs_metas_column_refresh_ = false;
628 }
629
630 if (needs_share_info_column_refresh_) {
631 // Repeat the process for share_info.
632 SafeDropTable("temp_share_info");
633 if (!CreateShareInfoTable(true))
634 return false;
635
636 if (!db_->Execute(
637 "INSERT INTO temp_share_info (id, name, store_birthday, "
638 "cache_guid, bag_of_chips) "
639 "SELECT id, name, store_birthday, cache_guid, bag_of_chips "
640 "FROM share_info"))
641 return false;
642
643 SafeDropTable("share_info");
644 if (!db_->Execute("ALTER TABLE temp_share_info RENAME TO share_info"))
645 return false;
646
647 needs_share_info_column_refresh_ = false;
648 }
649
650 return true;
651 }
652
653 bool DirectoryBackingStore::LoadEntries(Directory::MetahandlesMap* handles_map,
654 MetahandleSet* metahandles_to_purge) {
655 string select;
656 select.reserve(kUpdateStatementBufferSize);
657 select.append("SELECT ");
658 AppendColumnList(&select);
659 select.append(" FROM metas");
660 int total_specifics_copies = 0;
661 int model_type_entry_count[MODEL_TYPE_COUNT];
662 for (int i = 0; i < MODEL_TYPE_COUNT; ++i) {
663 model_type_entry_count[i] = 0;
664 }
665
666 sql::Statement s(db_->GetUniqueStatement(select.c_str()));
667
668 while (s.Step()) {
669 std::unique_ptr<EntryKernel> kernel =
670 UnpackEntry(&s, &total_specifics_copies);
671 // A null kernel is evidence of external data corruption.
672 if (!kernel)
673 return false;
674
675 int64_t handle = kernel->ref(META_HANDLE);
676 if (SafeToPurgeOnLoading(*kernel)) {
677 metahandles_to_purge->insert(handle);
678 } else {
679 ModelType model_type = kernel->GetModelType();
680 if (!IsRealDataType(model_type)) {
681 model_type = kernel->GetServerModelType();
682 }
683 ++model_type_entry_count[model_type];
684 (*handles_map)[handle] = kernel.release();
685 }
686 }
687
688 UploadModelTypeEntryCount(total_specifics_copies, model_type_entry_count);
689
690 return s.Succeeded();
691 }
692
693 bool DirectoryBackingStore::SafeToPurgeOnLoading(
694 const EntryKernel& entry) const {
695 if (entry.ref(IS_DEL)) {
696 if (!entry.ref(IS_UNSYNCED) && !entry.ref(IS_UNAPPLIED_UPDATE))
697 return true;
698 else if (!entry.ref(ID).ServerKnows())
699 return true;
700 }
701 return false;
702 }
703
704 bool DirectoryBackingStore::LoadDeleteJournals(
705 JournalIndex* delete_journals) {
706 string select;
707 select.reserve(kUpdateStatementBufferSize);
708 select.append("SELECT ");
709 AppendColumnList(&select);
710 select.append(" FROM deleted_metas");
711
712 sql::Statement s(db_->GetUniqueStatement(select.c_str()));
713
714 while (s.Step()) {
715 int total_entry_copies;
716 std::unique_ptr<EntryKernel> kernel = UnpackEntry(&s, &total_entry_copies);
717 // A null kernel is evidence of external data corruption.
718 if (!kernel)
719 return false;
720 delete_journals->insert(kernel.release());
721 }
722 return s.Succeeded();
723 }
724
725 bool DirectoryBackingStore::LoadInfo(Directory::KernelLoadInfo* info) {
726 {
727 sql::Statement s(db_->GetUniqueStatement(
728 "SELECT store_birthday, cache_guid, bag_of_chips "
729 "FROM share_info"));
730 if (!s.Step())
731 return false;
732
733 info->kernel_info.store_birthday = s.ColumnString(0);
734 info->cache_guid = s.ColumnString(1);
735 s.ColumnBlobAsString(2, &(info->kernel_info.bag_of_chips));
736
737 // Verify there was only one row returned.
738 DCHECK(!s.Step());
739 DCHECK(s.Succeeded());
740 }
741
742 {
743 sql::Statement s(
744 db_->GetUniqueStatement(
745 "SELECT model_id, progress_marker, "
746 "transaction_version, context FROM models"));
747
748 while (s.Step()) {
749 ModelType type = ModelIdToModelTypeEnum(s.ColumnBlob(0),
750 s.ColumnByteLength(0));
751 if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) {
752 info->kernel_info.download_progress[type].ParseFromArray(
753 s.ColumnBlob(1), s.ColumnByteLength(1));
754 info->kernel_info.transaction_version[type] = s.ColumnInt64(2);
755 info->kernel_info.datatype_context[type].ParseFromArray(
756 s.ColumnBlob(3), s.ColumnByteLength(3));
757 }
758 }
759 if (!s.Succeeded())
760 return false;
761 }
762 {
763 sql::Statement s(
764 db_->GetUniqueStatement(
765 "SELECT MAX(metahandle) FROM metas"));
766 if (!s.Step())
767 return false;
768
769 info->max_metahandle = s.ColumnInt64(0);
770
771 // Verify only one row was returned.
772 DCHECK(!s.Step());
773 DCHECK(s.Succeeded());
774 }
775 return true;
776 }
777
778 bool DirectoryBackingStore::SafeDropTable(const char* table_name) {
779 string query = "DROP TABLE IF EXISTS ";
780 query.append(table_name);
781 return db_->Execute(query.c_str());
782 }
783
784 void DirectoryBackingStore::DropAllTables() {
785 SafeDropTable("metas");
786 SafeDropTable("temp_metas");
787 SafeDropTable("share_info");
788 SafeDropTable("temp_share_info");
789 SafeDropTable("share_version");
790 SafeDropTable("extended_attributes");
791 SafeDropTable("models");
792 SafeDropTable("temp_models");
793 needs_metas_column_refresh_ = false;
794 needs_share_info_column_refresh_ = false;
795 }
796
797 // static
798 ModelType DirectoryBackingStore::ModelIdToModelTypeEnum(
799 const void* data, int size) {
800 sync_pb::EntitySpecifics specifics;
801 if (!specifics.ParseFromArray(data, size))
802 return UNSPECIFIED;
803 return GetModelTypeFromSpecifics(specifics);
804 }
805
806 // static
807 string DirectoryBackingStore::ModelTypeEnumToModelId(ModelType model_type) {
808 sync_pb::EntitySpecifics specifics;
809 AddDefaultFieldValue(model_type, &specifics);
810 return specifics.SerializeAsString();
811 }
812
813 // static
814 std::string DirectoryBackingStore::GenerateCacheGUID() {
815 // Generate a GUID with 128 bits of randomness.
816 const int kGuidBytes = 128 / 8;
817 std::string guid;
818 base::Base64Encode(base::RandBytesAsString(kGuidBytes), &guid);
819 return guid;
820 }
821
822 bool DirectoryBackingStore::MigrateToSpecifics(
823 const char* old_columns,
824 const char* specifics_column,
825 void (*handler_function)(sql::Statement* old_value_query,
826 int old_value_column,
827 sync_pb::EntitySpecifics* mutable_new_value)) {
828 std::string query_sql = base::StringPrintf(
829 "SELECT metahandle, %s, %s FROM metas", specifics_column, old_columns);
830 std::string update_sql = base::StringPrintf(
831 "UPDATE metas SET %s = ? WHERE metahandle = ?", specifics_column);
832
833 sql::Statement query(db_->GetUniqueStatement(query_sql.c_str()));
834 sql::Statement update(db_->GetUniqueStatement(update_sql.c_str()));
835
836 while (query.Step()) {
837 int64_t metahandle = query.ColumnInt64(0);
838 std::string new_value_bytes;
839 query.ColumnBlobAsString(1, &new_value_bytes);
840 sync_pb::EntitySpecifics new_value;
841 new_value.ParseFromString(new_value_bytes);
842 handler_function(&query, 2, &new_value);
843 new_value.SerializeToString(&new_value_bytes);
844
845 update.BindBlob(0, new_value_bytes.data(), new_value_bytes.length());
846 update.BindInt64(1, metahandle);
847 if (!update.Run())
848 return false;
849 update.Reset(true);
850 }
851 return query.Succeeded();
852 }
853
854 bool DirectoryBackingStore::SetVersion(int version) {
855 sql::Statement s(db_->GetCachedStatement(
856 SQL_FROM_HERE, "UPDATE share_version SET data = ?"));
857 s.BindInt(0, version);
858
859 return s.Run();
860 }
861
862 int DirectoryBackingStore::GetVersion() {
863 if (!db_->DoesTableExist("share_version"))
864 return 0;
865
866 sql::Statement statement(db_->GetUniqueStatement(
867 "SELECT data FROM share_version"));
868 if (statement.Step()) {
869 return statement.ColumnInt(0);
870 } else {
871 return 0;
872 }
873 }
874
875 bool DirectoryBackingStore::MigrateVersion67To68() {
876 // This change simply removed three columns:
877 // string NAME
878 // string UNSANITIZED_NAME
879 // string SERVER_NAME
880 // No data migration is necessary, but we should do a column refresh.
881 SetVersion(68);
882 needs_metas_column_refresh_ = true;
883 return true;
884 }
885
886 bool DirectoryBackingStore::MigrateVersion69To70() {
887 // Added "unique_client_tag", renamed "singleton_tag" to unique_server_tag
888 SetVersion(70);
889 if (!db_->Execute(
890 "ALTER TABLE metas ADD COLUMN unique_server_tag varchar"))
891 return false;
892 if (!db_->Execute(
893 "ALTER TABLE metas ADD COLUMN unique_client_tag varchar"))
894 return false;
895 needs_metas_column_refresh_ = true;
896
897 if (!db_->Execute(
898 "UPDATE metas SET unique_server_tag = singleton_tag"))
899 return false;
900
901 return true;
902 }
903
904 namespace {
905
906 // Callback passed to MigrateToSpecifics for the v68->v69 migration. See
907 // MigrateVersion68To69().
908 void EncodeBookmarkURLAndFavicon(sql::Statement* old_value_query,
909 int old_value_column,
910 sync_pb::EntitySpecifics* mutable_new_value) {
911 // Extract data from the column trio we expect.
912 bool old_is_bookmark_object = old_value_query->ColumnBool(old_value_column);
913 std::string old_url = old_value_query->ColumnString(old_value_column + 1);
914 std::string old_favicon;
915 old_value_query->ColumnBlobAsString(old_value_column + 2, &old_favicon);
916 bool old_is_dir = old_value_query->ColumnBool(old_value_column + 3);
917
918 if (old_is_bookmark_object) {
919 sync_pb::BookmarkSpecifics* bookmark_data =
920 mutable_new_value->mutable_bookmark();
921 if (!old_is_dir) {
922 bookmark_data->set_url(old_url);
923 bookmark_data->set_favicon(old_favicon);
924 }
925 }
926 }
927
928 } // namespace
929
930 bool DirectoryBackingStore::MigrateVersion68To69() {
931 // In Version 68, there were columns on table 'metas':
932 // string BOOKMARK_URL
933 // string SERVER_BOOKMARK_URL
934 // blob BOOKMARK_FAVICON
935 // blob SERVER_BOOKMARK_FAVICON
936 // In version 69, these columns went away in favor of storing
937 // a serialized EntrySpecifics protobuf in the columns:
938 // protobuf blob SPECIFICS
939 // protobuf blob SERVER_SPECIFICS
940 // For bookmarks, EntrySpecifics is extended as per
941 // bookmark_specifics.proto. This migration converts bookmarks from the
942 // former scheme to the latter scheme.
943
944 // First, add the two new columns to the schema.
945 if (!db_->Execute(
946 "ALTER TABLE metas ADD COLUMN specifics blob"))
947 return false;
948 if (!db_->Execute(
949 "ALTER TABLE metas ADD COLUMN server_specifics blob"))
950 return false;
951
952 // Next, fold data from the old columns into the new protobuf columns.
953 if (!MigrateToSpecifics(("is_bookmark_object, bookmark_url, "
954 "bookmark_favicon, is_dir"),
955 "specifics",
956 &EncodeBookmarkURLAndFavicon)) {
957 return false;
958 }
959 if (!MigrateToSpecifics(("server_is_bookmark_object, "
960 "server_bookmark_url, "
961 "server_bookmark_favicon, "
962 "server_is_dir"),
963 "server_specifics",
964 &EncodeBookmarkURLAndFavicon)) {
965 return false;
966 }
967
968 // Lastly, fix up the "Google Chrome" folder, which is of the TOP_LEVEL_FOLDER
969 // ModelType: it shouldn't have BookmarkSpecifics.
970 if (!db_->Execute(
971 "UPDATE metas SET specifics = NULL, server_specifics = NULL WHERE "
972 "singleton_tag IN ('google_chrome')"))
973 return false;
974
975 SetVersion(69);
976 needs_metas_column_refresh_ = true; // Trigger deletion of old columns.
977 return true;
978 }
979
980 // Version 71, the columns 'initial_sync_ended' and 'last_sync_timestamp'
981 // were removed from the share_info table. They were replaced by
982 // the 'models' table, which has these values on a per-datatype basis.
983 bool DirectoryBackingStore::MigrateVersion70To71() {
984 if (!CreateV71ModelsTable())
985 return false;
986
987 // Move data from the old share_info columns to the new models table.
988 {
989 sql::Statement fetch(db_->GetUniqueStatement(
990 "SELECT last_sync_timestamp, initial_sync_ended FROM share_info"));
991 if (!fetch.Step())
992 return false;
993
994 int64_t last_sync_timestamp = fetch.ColumnInt64(0);
995 bool initial_sync_ended = fetch.ColumnBool(1);
996
997 // Verify there were no additional rows returned.
998 DCHECK(!fetch.Step());
999 DCHECK(fetch.Succeeded());
1000
1001 sql::Statement update(db_->GetUniqueStatement(
1002 "INSERT INTO models (model_id, "
1003 "last_download_timestamp, initial_sync_ended) VALUES (?, ?, ?)"));
1004 string bookmark_model_id = ModelTypeEnumToModelId(BOOKMARKS);
1005 update.BindBlob(0, bookmark_model_id.data(), bookmark_model_id.size());
1006 update.BindInt64(1, last_sync_timestamp);
1007 update.BindBool(2, initial_sync_ended);
1008
1009 if (!update.Run())
1010 return false;
1011 }
1012
1013 // Drop the columns from the old share_info table via a temp table.
1014 const bool kCreateAsTempShareInfo = true;
1015
1016 if (!CreateShareInfoTableVersion71(kCreateAsTempShareInfo))
1017 return false;
1018 if (!db_->Execute(
1019 "INSERT INTO temp_share_info (id, name, store_birthday, "
1020 "db_create_version, db_create_time, next_id, cache_guid) "
1021 "SELECT id, name, store_birthday, db_create_version, "
1022 "db_create_time, next_id, cache_guid FROM share_info"))
1023 return false;
1024 SafeDropTable("share_info");
1025 if (!db_->Execute(
1026 "ALTER TABLE temp_share_info RENAME TO share_info"))
1027 return false;
1028 SetVersion(71);
1029 return true;
1030 }
1031
1032 bool DirectoryBackingStore::MigrateVersion71To72() {
1033 // Version 72 removed a table 'extended_attributes', whose
1034 // contents didn't matter.
1035 SafeDropTable("extended_attributes");
1036 SetVersion(72);
1037 return true;
1038 }
1039
1040 bool DirectoryBackingStore::MigrateVersion72To73() {
1041 // Version 73 added one column to the table 'share_info': notification_state
1042 if (!db_->Execute(
1043 "ALTER TABLE share_info ADD COLUMN notification_state BLOB"))
1044 return false;
1045 SetVersion(73);
1046 return true;
1047 }
1048
1049 bool DirectoryBackingStore::MigrateVersion73To74() {
1050 // Version 74 added the following columns to the table 'share_info':
1051 // autofill_migration_state
1052 // bookmarks_added_during_autofill_migration
1053 // autofill_migration_time
1054 // autofill_entries_added_during_migration
1055 // autofill_profiles_added_during_migration
1056
1057 if (!db_->Execute(
1058 "ALTER TABLE share_info ADD COLUMN "
1059 "autofill_migration_state INT default 0"))
1060 return false;
1061
1062 if (!db_->Execute(
1063 "ALTER TABLE share_info ADD COLUMN "
1064 "bookmarks_added_during_autofill_migration "
1065 "INT default 0"))
1066 return false;
1067
1068 if (!db_->Execute(
1069 "ALTER TABLE share_info ADD COLUMN autofill_migration_time "
1070 "INT default 0"))
1071 return false;
1072
1073 if (!db_->Execute(
1074 "ALTER TABLE share_info ADD COLUMN "
1075 "autofill_entries_added_during_migration "
1076 "INT default 0"))
1077 return false;
1078
1079 if (!db_->Execute(
1080 "ALTER TABLE share_info ADD COLUMN "
1081 "autofill_profiles_added_during_migration "
1082 "INT default 0"))
1083 return false;
1084
1085 SetVersion(74);
1086 return true;
1087 }
1088
1089 bool DirectoryBackingStore::MigrateVersion74To75() {
1090 // In version 74, there was a table 'models':
1091 // blob model_id (entity specifics, primary key)
1092 // int last_download_timestamp
1093 // boolean initial_sync_ended
1094 // In version 75, we deprecated the integer-valued last_download_timestamp,
1095 // using insted a protobuf-valued progress_marker field:
1096 // blob progress_marker
1097 // The progress_marker values are initialized from the value of
1098 // last_download_timestamp, thereby preserving the download state.
1099
1100 // Move aside the old table and create a new empty one at the current schema.
1101 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models"))
1102 return false;
1103 if (!CreateV75ModelsTable())
1104 return false;
1105
1106 sql::Statement query(db_->GetUniqueStatement(
1107 "SELECT model_id, last_download_timestamp, initial_sync_ended "
1108 "FROM temp_models"));
1109
1110 sql::Statement update(db_->GetUniqueStatement(
1111 "INSERT INTO models (model_id, "
1112 "progress_marker, initial_sync_ended) VALUES (?, ?, ?)"));
1113
1114 while (query.Step()) {
1115 ModelType type = ModelIdToModelTypeEnum(query.ColumnBlob(0),
1116 query.ColumnByteLength(0));
1117 if (type != UNSPECIFIED) {
1118 // Set the |timestamp_token_for_migration| on a new
1119 // DataTypeProgressMarker, using the old value of last_download_timestamp.
1120 // The server will turn this into a real token on our behalf the next
1121 // time we check for updates.
1122 sync_pb::DataTypeProgressMarker progress_marker;
1123 progress_marker.set_data_type_id(
1124 GetSpecificsFieldNumberFromModelType(type));
1125 progress_marker.set_timestamp_token_for_migration(query.ColumnInt64(1));
1126 std::string progress_blob;
1127 progress_marker.SerializeToString(&progress_blob);
1128
1129 update.BindBlob(0, query.ColumnBlob(0), query.ColumnByteLength(0));
1130 update.BindBlob(1, progress_blob.data(), progress_blob.length());
1131 update.BindBool(2, query.ColumnBool(2));
1132 if (!update.Run())
1133 return false;
1134 update.Reset(true);
1135 }
1136 }
1137 if (!query.Succeeded())
1138 return false;
1139
1140 // Drop the old table.
1141 SafeDropTable("temp_models");
1142
1143 SetVersion(75);
1144 return true;
1145 }
1146
1147 bool DirectoryBackingStore::MigrateVersion75To76() {
1148 // This change removed five columns:
1149 // autofill_migration_state
1150 // bookmarks_added_during_autofill_migration
1151 // autofill_migration_time
1152 // autofill_entries_added_during_migration
1153 // autofill_profiles_added_during_migration
1154 // No data migration is necessary, but we should do a column refresh.
1155 SetVersion(76);
1156 needs_share_info_column_refresh_ = true;
1157 return true;
1158 }
1159
1160 bool DirectoryBackingStore::MigrateVersion76To77() {
1161 // This change changes the format of stored timestamps to ms since
1162 // the Unix epoch.
1163 #if defined(OS_WIN)
1164 // On Windows, we used to store timestamps in FILETIME format (100s of
1165 // ns since Jan 1, 1601). Magic numbers taken from
1166 // http://stackoverflow.com/questions/5398557/
1167 // java-library-for-dealing-with-win32-filetime
1168 // .
1169 #define TO_UNIX_TIME_MS(x) #x " = " #x " / 10000 - 11644473600000"
1170 #else
1171 // On other platforms, we used to store timestamps in time_t format (s
1172 // since the Unix epoch).
1173 #define TO_UNIX_TIME_MS(x) #x " = " #x " * 1000"
1174 #endif
1175 sql::Statement update_timestamps(db_->GetUniqueStatement(
1176 "UPDATE metas SET "
1177 TO_UNIX_TIME_MS(mtime) ", "
1178 TO_UNIX_TIME_MS(server_mtime) ", "
1179 TO_UNIX_TIME_MS(ctime) ", "
1180 TO_UNIX_TIME_MS(server_ctime)));
1181 #undef TO_UNIX_TIME_MS
1182 if (!update_timestamps.Run())
1183 return false;
1184 SetVersion(77);
1185 return true;
1186 }
1187
1188 bool DirectoryBackingStore::MigrateVersion77To78() {
1189 // Version 78 added one column to table 'metas': base_server_specifics.
1190 if (!db_->Execute(
1191 "ALTER TABLE metas ADD COLUMN base_server_specifics BLOB")) {
1192 return false;
1193 }
1194 SetVersion(78);
1195 return true;
1196 }
1197
1198 bool DirectoryBackingStore::MigrateVersion78To79() {
1199 // Some users are stuck with a DB that causes them to reuse existing IDs. We
1200 // perform this one-time fixup on all users to help the few that are stuck.
1201 // See crbug.com/142987 for details.
1202 if (!db_->Execute(
1203 "UPDATE share_info SET next_id = next_id - 65536")) {
1204 return false;
1205 }
1206 SetVersion(79);
1207 return true;
1208 }
1209
1210 bool DirectoryBackingStore::MigrateVersion79To80() {
1211 if (!db_->Execute(
1212 "ALTER TABLE share_info ADD COLUMN bag_of_chips BLOB"))
1213 return false;
1214 sql::Statement update(db_->GetUniqueStatement(
1215 "UPDATE share_info SET bag_of_chips = ?"));
1216 // An empty message is serialized to an empty string.
1217 update.BindBlob(0, NULL, 0);
1218 if (!update.Run())
1219 return false;
1220 SetVersion(80);
1221 return true;
1222 }
1223
1224 bool DirectoryBackingStore::MigrateVersion80To81() {
1225 if (!db_->Execute(
1226 "ALTER TABLE metas ADD COLUMN server_ordinal_in_parent BLOB"))
1227 return false;
1228
1229 sql::Statement get_positions(db_->GetUniqueStatement(
1230 "SELECT metahandle, server_position_in_parent FROM metas"));
1231
1232 sql::Statement put_ordinals(db_->GetUniqueStatement(
1233 "UPDATE metas SET server_ordinal_in_parent = ?"
1234 "WHERE metahandle = ?"));
1235
1236 while (get_positions.Step()) {
1237 int64_t metahandle = get_positions.ColumnInt64(0);
1238 int64_t position = get_positions.ColumnInt64(1);
1239
1240 const std::string& ordinal = Int64ToNodeOrdinal(position).ToInternalValue();
1241 put_ordinals.BindBlob(0, ordinal.data(), ordinal.length());
1242 put_ordinals.BindInt64(1, metahandle);
1243
1244 if (!put_ordinals.Run())
1245 return false;
1246 put_ordinals.Reset(true);
1247 }
1248
1249 SetVersion(81);
1250 needs_metas_column_refresh_ = true;
1251 return true;
1252 }
1253
1254 bool DirectoryBackingStore::MigrateVersion81To82() {
1255 if (!db_->Execute(
1256 "ALTER TABLE models ADD COLUMN transaction_version BIGINT default 0"))
1257 return false;
1258 sql::Statement update(db_->GetUniqueStatement(
1259 "UPDATE models SET transaction_version = 0"));
1260 if (!update.Run())
1261 return false;
1262 SetVersion(82);
1263 return true;
1264 }
1265
1266 bool DirectoryBackingStore::MigrateVersion82To83() {
1267 // Version 83 added transaction_version on sync node.
1268 if (!db_->Execute(
1269 "ALTER TABLE metas ADD COLUMN transaction_version BIGINT default 0"))
1270 return false;
1271 sql::Statement update(db_->GetUniqueStatement(
1272 "UPDATE metas SET transaction_version = 0"));
1273 if (!update.Run())
1274 return false;
1275 SetVersion(83);
1276 return true;
1277 }
1278
1279 bool DirectoryBackingStore::MigrateVersion83To84() {
1280 // Version 84 added deleted_metas table to store deleted metas until we know
1281 // for sure that the deletions are persisted in native models.
1282 string query = "CREATE TABLE deleted_metas ";
1283 query.append(ComposeCreateTableColumnSpecs());
1284 if (!db_->Execute(query.c_str()))
1285 return false;
1286 SetVersion(84);
1287 return true;
1288 }
1289
1290 bool DirectoryBackingStore::MigrateVersion84To85() {
1291 // Version 85 removes the initial_sync_ended flag.
1292 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models"))
1293 return false;
1294 if (!CreateV81ModelsTable())
1295 return false;
1296 if (!db_->Execute("INSERT INTO models SELECT "
1297 "model_id, progress_marker, transaction_version "
1298 "FROM temp_models")) {
1299 return false;
1300 }
1301 SafeDropTable("temp_models");
1302
1303 SetVersion(85);
1304 return true;
1305 }
1306
1307 bool DirectoryBackingStore::MigrateVersion85To86() {
1308 // Version 86 removes both server ordinals and local NEXT_ID, PREV_ID and
1309 // SERVER_{POSITION,ORDINAL}_IN_PARENT and replaces them with UNIQUE_POSITION
1310 // and SERVER_UNIQUE_POSITION.
1311 if (!db_->Execute("ALTER TABLE metas ADD COLUMN "
1312 "server_unique_position BLOB")) {
1313 return false;
1314 }
1315 if (!db_->Execute("ALTER TABLE metas ADD COLUMN "
1316 "unique_position BLOB")) {
1317 return false;
1318 }
1319 if (!db_->Execute("ALTER TABLE metas ADD COLUMN "
1320 "unique_bookmark_tag VARCHAR")) {
1321 return false;
1322 }
1323
1324 // Fetch the cache_guid from the DB, because we don't otherwise have access to
1325 // it from here.
1326 sql::Statement get_cache_guid(db_->GetUniqueStatement(
1327 "SELECT cache_guid FROM share_info"));
1328 if (!get_cache_guid.Step()) {
1329 return false;
1330 }
1331 std::string cache_guid = get_cache_guid.ColumnString(0);
1332 DCHECK(!get_cache_guid.Step());
1333 DCHECK(get_cache_guid.Succeeded());
1334
1335 sql::Statement get(db_->GetUniqueStatement(
1336 "SELECT "
1337 " metahandle, "
1338 " id, "
1339 " specifics, "
1340 " is_dir, "
1341 " unique_server_tag, "
1342 " server_ordinal_in_parent "
1343 "FROM metas"));
1344
1345 // Note that we set both the local and server position based on the server
1346 // position. We wll lose any unsynced local position changes. Unfortunately,
1347 // there's nothing we can do to avoid that. The NEXT_ID / PREV_ID values
1348 // can't be translated into a UNIQUE_POSTION in a reliable way.
1349 sql::Statement put(db_->GetCachedStatement(
1350 SQL_FROM_HERE,
1351 "UPDATE metas SET"
1352 " server_unique_position = ?,"
1353 " unique_position = ?,"
1354 " unique_bookmark_tag = ?"
1355 "WHERE metahandle = ?"));
1356
1357 while (get.Step()) {
1358 int64_t metahandle = get.ColumnInt64(0);
1359
1360 std::string id_string;
1361 get.ColumnBlobAsString(1, &id_string);
1362
1363 sync_pb::EntitySpecifics specifics;
1364 specifics.ParseFromArray(
1365 get.ColumnBlob(2), get.ColumnByteLength(2));
1366
1367 bool is_dir = get.ColumnBool(3);
1368
1369 std::string server_unique_tag = get.ColumnString(4);
1370
1371 std::string ordinal_string;
1372 get.ColumnBlobAsString(5, &ordinal_string);
1373 NodeOrdinal ordinal(ordinal_string);
1374
1375
1376 std::string unique_bookmark_tag;
1377
1378 // We only maintain positions for bookmarks that are not server-defined
1379 // top-level folders.
1380 UniquePosition position;
1381 if (GetModelTypeFromSpecifics(specifics) == BOOKMARKS
1382 && !(is_dir && !server_unique_tag.empty())) {
1383 if (id_string.at(0) == 'c') {
1384 // We found an uncommitted item. This is rare, but fortunate. This
1385 // means we can set the bookmark tag according to the originator client
1386 // item ID and originator cache guid, because (unlike the other case) we
1387 // know that this client is the originator.
1388 unique_bookmark_tag = syncable::GenerateSyncableBookmarkHash(
1389 cache_guid,
1390 id_string.substr(1));
1391 } else {
1392 // If we've already committed the item, then we don't know who the
1393 // originator was. We do not have access to the originator client item
1394 // ID and originator cache guid at this point.
1395 //
1396 // We will base our hash entirely on the server ID instead. This is
1397 // incorrect, but at least all clients that undergo this migration step
1398 // will be incorrect in the same way.
1399 //
1400 // To get everyone back into a synced state, we will update the bookmark
1401 // tag according to the originator_cache_guid and originator_item_id
1402 // when we see updates for this item. That should ensure that commonly
1403 // modified items will end up with the proper tag values eventually.
1404 unique_bookmark_tag = syncable::GenerateSyncableBookmarkHash(
1405 std::string(), // cache_guid left intentionally blank.
1406 id_string.substr(1));
1407 }
1408
1409 int64_t int_position = NodeOrdinalToInt64(ordinal);
1410 position = UniquePosition::FromInt64(int_position, unique_bookmark_tag);
1411 } else {
1412 // Leave bookmark_tag and position at their default (invalid) values.
1413 }
1414
1415 std::string position_blob;
1416 position.SerializeToString(&position_blob);
1417 put.BindBlob(0, position_blob.data(), position_blob.length());
1418 put.BindBlob(1, position_blob.data(), position_blob.length());
1419 put.BindBlob(2, unique_bookmark_tag.data(), unique_bookmark_tag.length());
1420 put.BindInt64(3, metahandle);
1421
1422 if (!put.Run())
1423 return false;
1424 put.Reset(true);
1425 }
1426
1427 SetVersion(86);
1428 needs_metas_column_refresh_ = true;
1429 return true;
1430 }
1431
1432 bool DirectoryBackingStore::MigrateVersion86To87() {
1433 // Version 87 adds AttachmentMetadata proto.
1434 if (!db_->Execute(
1435 "ALTER TABLE metas ADD COLUMN "
1436 "attachment_metadata BLOB")) {
1437 return false;
1438 }
1439 SetVersion(87);
1440 needs_metas_column_refresh_ = true;
1441 return true;
1442 }
1443
1444 bool DirectoryBackingStore::MigrateVersion87To88() {
1445 // Version 88 adds the datatype context to the models table.
1446 if (!db_->Execute("ALTER TABLE models ADD COLUMN context blob"))
1447 return false;
1448
1449 SetVersion(88);
1450 return true;
1451 }
1452
1453 bool DirectoryBackingStore::MigrateVersion88To89() {
1454 // Version 89 adds server_attachment_metadata.
1455 if (!db_->Execute(
1456 "ALTER TABLE metas ADD COLUMN "
1457 "server_attachment_metadata BLOB")) {
1458 return false;
1459 }
1460 SetVersion(89);
1461 needs_metas_column_refresh_ = true;
1462 return true;
1463 }
1464
1465 bool DirectoryBackingStore::MigrateVersion89To90() {
1466 // This change removed 4 columns from meta_info:
1467 // db_create_version
1468 // db_create_time
1469 // next_id
1470 // notification_state
1471 // No data migration is necessary, but we should do a column refresh.
1472 SetVersion(90);
1473 needs_share_info_column_refresh_ = true;
1474 return true;
1475 }
1476
1477 bool DirectoryBackingStore::CreateTables() {
1478 DVLOG(1) << "First run, creating tables";
1479
1480 // Create two little tables share_version and share_info
1481 if (!db_->Execute(
1482 "CREATE TABLE share_version ("
1483 "id VARCHAR(128) primary key, data INT)")) {
1484 return false;
1485 }
1486
1487 {
1488 sql::Statement s(db_->GetUniqueStatement(
1489 "INSERT INTO share_version VALUES(?, ?)"));
1490 s.BindString(0, dir_name_);
1491 s.BindInt(1, kCurrentDBVersion);
1492
1493 if (!s.Run())
1494 return false;
1495 }
1496
1497 const bool kCreateAsTempShareInfo = false;
1498 if (!CreateShareInfoTable(kCreateAsTempShareInfo)) {
1499 return false;
1500 }
1501
1502 {
1503 sql::Statement s(db_->GetUniqueStatement(
1504 "INSERT INTO share_info VALUES"
1505 "(?, " // id
1506 "?, " // name
1507 "?, " // store_birthday
1508 "?, " // cache_guid
1509 "?);")); // bag_of_chips
1510 s.BindString(0, dir_name_); // id
1511 s.BindString(1, dir_name_); // name
1512 s.BindString(2, std::string()); // store_birthday
1513 s.BindString(3, GenerateCacheGUID()); // cache_guid
1514 s.BindBlob(4, NULL, 0); // bag_of_chips
1515 if (!s.Run())
1516 return false;
1517 }
1518
1519 if (!CreateModelsTable())
1520 return false;
1521
1522 // Create the big metas table.
1523 if (!CreateMetasTable(false))
1524 return false;
1525
1526 {
1527 // Insert the entry for the root into the metas table.
1528 const int64_t now = TimeToProtoTime(base::Time::Now());
1529 sql::Statement s(db_->GetUniqueStatement(
1530 "INSERT INTO metas "
1531 "( id, metahandle, is_dir, ctime, mtime ) "
1532 "VALUES ( \"r\", 1, 1, ?, ? )"));
1533 s.BindInt64(0, now);
1534 s.BindInt64(1, now);
1535
1536 if (!s.Run())
1537 return false;
1538 }
1539
1540 return true;
1541 }
1542
1543 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) {
1544 string query = "CREATE TABLE ";
1545 query.append(is_temporary ? "temp_metas" : "metas");
1546 query.append(ComposeCreateTableColumnSpecs());
1547 if (!db_->Execute(query.c_str()))
1548 return false;
1549
1550 // Create a deleted_metas table to save copies of deleted metas until the
1551 // deletions are persisted. For simplicity, don't try to migrate existing
1552 // data because it's rarely used.
1553 SafeDropTable("deleted_metas");
1554 query = "CREATE TABLE deleted_metas ";
1555 query.append(ComposeCreateTableColumnSpecs());
1556 return db_->Execute(query.c_str());
1557 }
1558
1559 bool DirectoryBackingStore::CreateV71ModelsTable() {
1560 // This is an old schema for the Models table, used from versions 71 to 74.
1561 return db_->Execute(
1562 "CREATE TABLE models ("
1563 "model_id BLOB primary key, "
1564 "last_download_timestamp INT, "
1565 // Gets set if the syncer ever gets updates from the
1566 // server and the server returns 0. Lets us detect the
1567 // end of the initial sync.
1568 "initial_sync_ended BOOLEAN default 0)");
1569 }
1570
1571 bool DirectoryBackingStore::CreateV75ModelsTable() {
1572 // This is an old schema for the Models table, used from versions 75 to 80.
1573 return db_->Execute(
1574 "CREATE TABLE models ("
1575 "model_id BLOB primary key, "
1576 "progress_marker BLOB, "
1577 // Gets set if the syncer ever gets updates from the
1578 // server and the server returns 0. Lets us detect the
1579 // end of the initial sync.
1580 "initial_sync_ended BOOLEAN default 0)");
1581 }
1582
1583 bool DirectoryBackingStore::CreateV81ModelsTable() {
1584 // This is an old schema for the Models table, used from versions 81 to 87.
1585 return db_->Execute(
1586 "CREATE TABLE models ("
1587 "model_id BLOB primary key, "
1588 "progress_marker BLOB, "
1589 // Gets set if the syncer ever gets updates from the
1590 // server and the server returns 0. Lets us detect the
1591 // end of the initial sync.
1592 "transaction_version BIGINT default 0)");
1593 }
1594
1595 bool DirectoryBackingStore::CreateModelsTable() {
1596 // This is the current schema for the Models table, from version 88
1597 // onward. If you change the schema, you'll probably want to double-check
1598 // the use of this function in the v84-v85 migration.
1599 return db_->Execute(
1600 "CREATE TABLE models ("
1601 "model_id BLOB primary key, "
1602 "progress_marker BLOB, "
1603 // Gets set if the syncer ever gets updates from the
1604 // server and the server returns 0. Lets us detect the
1605 // end of the initial sync.
1606 "transaction_version BIGINT default 0,"
1607 "context BLOB)");
1608 }
1609
1610 bool DirectoryBackingStore::CreateShareInfoTable(bool is_temporary) {
1611 const char* name = is_temporary ? "temp_share_info" : "share_info";
1612 string query = "CREATE TABLE ";
1613 query.append(name);
1614 // This is the current schema for the ShareInfo table, from version 76
1615 // onward.
1616 query.append(" ("
1617 "id TEXT primary key, "
1618 "name TEXT, "
1619 "store_birthday TEXT, "
1620 "cache_guid TEXT, "
1621 "bag_of_chips BLOB"
1622 ")");
1623 return db_->Execute(query.c_str());
1624 }
1625
1626 bool DirectoryBackingStore::CreateShareInfoTableVersion71(
1627 bool is_temporary) {
1628 const char* name = is_temporary ? "temp_share_info" : "share_info";
1629 string query = "CREATE TABLE ";
1630 query.append(name);
1631 // This is the schema for the ShareInfo table used from versions 71 to 72.
1632 query.append(" ("
1633 "id TEXT primary key, "
1634 "name TEXT, "
1635 "store_birthday TEXT, "
1636 "db_create_version TEXT, "
1637 "db_create_time INT, "
1638 "next_id INT default -2, "
1639 "cache_guid TEXT )");
1640 return db_->Execute(query.c_str());
1641 }
1642
1643 // This function checks to see if the given list of Metahandles has any nodes
1644 // whose PARENT_ID values refer to ID values that do not actually exist.
1645 // Returns true on success.
1646 bool DirectoryBackingStore::VerifyReferenceIntegrity(
1647 const Directory::MetahandlesMap* handles_map) {
1648 TRACE_EVENT0("sync", "SyncDatabaseIntegrityCheck");
1649 typedef std::unordered_set<std::string> IdsSet;
1650
1651 IdsSet ids_set;
1652 bool is_ok = true;
1653
1654 for (Directory::MetahandlesMap::const_iterator it = handles_map->begin();
1655 it != handles_map->end(); ++it) {
1656 EntryKernel* entry = it->second;
1657 bool is_duplicate_id = !(ids_set.insert(entry->ref(ID).value()).second);
1658 is_ok = is_ok && !is_duplicate_id;
1659 }
1660
1661 IdsSet::iterator end = ids_set.end();
1662 for (Directory::MetahandlesMap::const_iterator it = handles_map->begin();
1663 it != handles_map->end(); ++it) {
1664 EntryKernel* entry = it->second;
1665 if (!entry->ref(PARENT_ID).IsNull()) {
1666 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end);
1667 if (!parent_exists) {
1668 return false;
1669 }
1670 }
1671 }
1672 return is_ok;
1673 }
1674
1675 void DirectoryBackingStore::PrepareSaveEntryStatement(
1676 EntryTable table, sql::Statement* save_statement) {
1677 if (save_statement->is_valid())
1678 return;
1679
1680 string query;
1681 query.reserve(kUpdateStatementBufferSize);
1682 switch (table) {
1683 case METAS_TABLE:
1684 query.append("INSERT OR REPLACE INTO metas ");
1685 break;
1686 case DELETE_JOURNAL_TABLE:
1687 query.append("INSERT OR REPLACE INTO deleted_metas ");
1688 break;
1689 }
1690
1691 string values;
1692 values.reserve(kUpdateStatementBufferSize);
1693 values.append(" VALUES ");
1694 const char* separator = "( ";
1695 int i = 0;
1696 for (i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) {
1697 query.append(separator);
1698 values.append(separator);
1699 separator = ", ";
1700 query.append(ColumnName(i));
1701 values.append("?");
1702 }
1703 query.append(" ) ");
1704 values.append(" )");
1705 query.append(values);
1706 save_statement->Assign(db_->GetUniqueStatement(
1707 base::StringPrintf(query.c_str(), "metas").c_str()));
1708 }
1709
1710 // Get page size for the database.
1711 bool DirectoryBackingStore::GetDatabasePageSize(int* page_size) {
1712 sql::Statement s(db_->GetUniqueStatement("PRAGMA page_size"));
1713 if (!s.Step())
1714 return false;
1715 *page_size = s.ColumnInt(0);
1716 return true;
1717 }
1718
1719 bool DirectoryBackingStore::UpdatePageSizeIfNecessary() {
1720 int page_size;
1721 if (!GetDatabasePageSize(&page_size))
1722 return false;
1723 if (page_size == kCurrentPageSizeKB)
1724 return true;
1725 std::string update_page_size = base::StringPrintf(
1726 "PRAGMA page_size=%i;", kCurrentPageSizeKB);
1727 if (!db_->Execute(update_page_size.c_str()) || !Vacuum())
1728 return false;
1729 return true;
1730 }
1731
1732 bool DirectoryBackingStore::Vacuum() {
1733 DCHECK_EQ(db_->transaction_nesting(), 0);
1734 if (!db_->Execute("VACUUM;")) {
1735 return false;
1736 }
1737 return true;
1738 }
1739
1740 bool DirectoryBackingStore::needs_column_refresh() const {
1741 return needs_metas_column_refresh_ || needs_share_info_column_refresh_;
1742 }
1743
1744 void DirectoryBackingStore::ResetAndCreateConnection() {
1745 db_.reset(new sql::Connection());
1746 db_->set_histogram_tag("SyncDirectory");
1747 db_->set_exclusive_locking();
1748 db_->set_cache_size(32);
1749 db_->set_page_size(database_page_size_);
1750
1751 // TODO(shess): The current mitigation for http://crbug.com/537742 stores
1752 // state in the meta table, which this database does not use.
1753 db_->set_mmap_disabled();
1754
1755 if (!catastrophic_error_handler_.is_null())
1756 SetCatastrophicErrorHandler(catastrophic_error_handler_);
1757 }
1758
1759 void DirectoryBackingStore::SetCatastrophicErrorHandler(
1760 const base::Closure& catastrophic_error_handler) {
1761 DCHECK(CalledOnValidThread());
1762 DCHECK(!catastrophic_error_handler.is_null());
1763 catastrophic_error_handler_ = catastrophic_error_handler;
1764 sql::Connection::ErrorCallback error_callback =
1765 base::Bind(&OnSqliteError, catastrophic_error_handler_);
1766 db_->set_error_callback(error_callback);
1767 }
1768
1769 } // namespace syncable
1770 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/syncable/directory_backing_store.h ('k') | sync/syncable/directory_backing_store_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698