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