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