• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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 "chrome/browser/sync/syncable/directory_backing_store.h"
6 
7 #include "build/build_config.h"
8 
9 #if defined(OS_MACOSX)
10 #include <CoreFoundation/CoreFoundation.h>
11 #endif
12 
13 #include <limits>
14 
15 #include "base/file_util.h"
16 #include "base/hash_tables.h"
17 #include "base/logging.h"
18 #include "base/metrics/histogram.h"
19 #include "base/stl_util-inl.h"
20 #include "base/string_number_conversions.h"
21 #include "base/string_util.h"
22 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
23 #include "chrome/browser/sync/protocol/service_constants.h"
24 #include "chrome/browser/sync/protocol/sync.pb.h"
25 #include "chrome/browser/sync/syncable/syncable-inl.h"
26 #include "chrome/browser/sync/syncable/syncable_columns.h"
27 #include "chrome/browser/sync/util/crypto_helpers.h"
28 #include "chrome/common/sqlite_utils.h"
29 #include "third_party/sqlite/sqlite3.h"
30 
31 // Sometimes threads contend on the DB lock itself, especially when one thread
32 // is calling SaveChanges.  In the worst case scenario, the user can put his
33 // laptop to sleep during db contention, and wake up the laptop days later, so
34 // infinity seems like the best choice here.
35 const int kDirectoryBackingStoreBusyTimeoutMs = std::numeric_limits<int>::max();
36 
37 using std::string;
38 
39 namespace syncable {
40 
41 // This just has to be big enough to hold an UPDATE or INSERT statement that
42 // modifies all the columns in the entry table.
43 static const string::size_type kUpdateStatementBufferSize = 2048;
44 
45 // Increment this version whenever updating DB tables.
46 extern const int32 kCurrentDBVersion;  // Global visibility for our unittest.
47 const int32 kCurrentDBVersion = 75;
48 
49 namespace {
50 
ExecQuery(sqlite3 * dbhandle,const char * query)51 int ExecQuery(sqlite3* dbhandle, const char* query) {
52   SQLStatement statement;
53   int result = statement.prepare(dbhandle, query);
54   if (SQLITE_OK != result)
55     return result;
56   do {
57     result = statement.step();
58   } while (SQLITE_ROW == result);
59 
60   return result;
61 }
62 
GenerateCacheGUID()63 string GenerateCacheGUID() {
64   return Generate128BitRandomHexString();
65 }
66 
67 }  // namespace
68 
69 
70 // Iterate over the fields of |entry| and bind each to |statement| for
71 // updating.  Returns the number of args bound.
BindFields(const EntryKernel & entry,SQLStatement * statement)72 int BindFields(const EntryKernel& entry, SQLStatement* statement) {
73   int index = 0;
74   int i = 0;
75   for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
76     statement->bind_int64(index++, entry.ref(static_cast<Int64Field>(i)));
77   }
78   for ( ; i < ID_FIELDS_END; ++i) {
79     statement->bind_string(index++, entry.ref(static_cast<IdField>(i)).s_);
80   }
81   for ( ; i < BIT_FIELDS_END; ++i) {
82     statement->bind_int(index++, entry.ref(static_cast<BitField>(i)));
83   }
84   for ( ; i < STRING_FIELDS_END; ++i) {
85     statement->bind_string(index++, entry.ref(static_cast<StringField>(i)));
86   }
87   std::string temp;
88   for ( ; i < PROTO_FIELDS_END; ++i) {
89     entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp);
90     statement->bind_blob(index++, temp.data(), temp.length());
91   }
92   return index;
93 }
94 
95 // The caller owns the returned EntryKernel*.
UnpackEntry(SQLStatement * statement,EntryKernel ** kernel)96 int UnpackEntry(SQLStatement* statement, EntryKernel** kernel) {
97   *kernel = NULL;
98   int query_result = statement->step();
99   if (SQLITE_ROW == query_result) {
100     *kernel = new EntryKernel;
101     (*kernel)->clear_dirty(NULL);
102     DCHECK(statement->column_count() == static_cast<int>(FIELD_COUNT));
103     int i = 0;
104     for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
105       (*kernel)->put(static_cast<Int64Field>(i), statement->column_int64(i));
106     }
107     for ( ; i < ID_FIELDS_END; ++i) {
108       (*kernel)->mutable_ref(static_cast<IdField>(i)).s_ =
109           statement->column_string(i);
110     }
111     for ( ; i < BIT_FIELDS_END; ++i) {
112       (*kernel)->put(static_cast<BitField>(i), (0 != statement->column_int(i)));
113     }
114     for ( ; i < STRING_FIELDS_END; ++i) {
115       (*kernel)->put(static_cast<StringField>(i),
116           statement->column_string(i));
117     }
118     for ( ; i < PROTO_FIELDS_END; ++i) {
119       (*kernel)->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray(
120           statement->column_blob(i), statement->column_bytes(i));
121     }
122     ZeroFields((*kernel), i);
123   } else {
124     DCHECK(SQLITE_DONE == query_result);
125     (*kernel) = NULL;
126   }
127   return query_result;
128 }
129 
130 namespace {
131 
ComposeCreateTableColumnSpecs()132 string ComposeCreateTableColumnSpecs() {
133   const ColumnSpec* begin = g_metas_columns;
134   const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns);
135   string query;
136   query.reserve(kUpdateStatementBufferSize);
137   char separator = '(';
138   for (const ColumnSpec* column = begin; column != end; ++column) {
139     query.push_back(separator);
140     separator = ',';
141     query.append(column->name);
142     query.push_back(' ');
143     query.append(column->spec);
144   }
145   query.push_back(')');
146   return query;
147 }
148 
AppendColumnList(std::string * output)149 void AppendColumnList(std::string* output) {
150   const char* joiner = " ";
151   // Be explicit in SELECT order to match up with UnpackEntry.
152   for (int i = BEGIN_FIELDS; i < BEGIN_FIELDS + FIELD_COUNT; ++i) {
153     output->append(joiner);
154     output->append(ColumnName(i));
155     joiner = ", ";
156   }
157 }
158 
159 }  // namespace
160 
161 ///////////////////////////////////////////////////////////////////////////////
162 // DirectoryBackingStore implementation.
163 
DirectoryBackingStore(const string & dir_name,const FilePath & backing_filepath)164 DirectoryBackingStore::DirectoryBackingStore(const string& dir_name,
165                                              const FilePath& backing_filepath)
166     : load_dbhandle_(NULL),
167       save_dbhandle_(NULL),
168       dir_name_(dir_name),
169       backing_filepath_(backing_filepath),
170       needs_column_refresh_(false) {
171 }
172 
~DirectoryBackingStore()173 DirectoryBackingStore::~DirectoryBackingStore() {
174   if (NULL != load_dbhandle_) {
175     sqlite3_close(load_dbhandle_);
176     load_dbhandle_ = NULL;
177   }
178   if (NULL != save_dbhandle_) {
179     sqlite3_close(save_dbhandle_);
180     save_dbhandle_ = NULL;
181   }
182 }
183 
OpenAndConfigureHandleHelper(sqlite3 ** handle) const184 bool DirectoryBackingStore::OpenAndConfigureHandleHelper(
185     sqlite3** handle) const {
186   if (SQLITE_OK == sqlite_utils::OpenSqliteDb(backing_filepath_, handle)) {
187     sqlite_utils::scoped_sqlite_db_ptr scoped_handle(*handle);
188     sqlite3_busy_timeout(scoped_handle.get(), std::numeric_limits<int>::max());
189     {
190       string integrity_error;
191       bool is_ok = CheckIntegrity(scoped_handle.get(), &integrity_error);
192       if (!is_ok) {
193         LOG(ERROR) << "Integrity check failed: " << integrity_error;
194         return false;
195       }
196     }
197     {
198       SQLStatement statement;
199       statement.prepare(scoped_handle.get(), "PRAGMA fullfsync = 1");
200       if (SQLITE_DONE != statement.step()) {
201         LOG(ERROR) << sqlite3_errmsg(scoped_handle.get());
202         return false;
203       }
204     }
205     {
206       SQLStatement statement;
207       statement.prepare(scoped_handle.get(), "PRAGMA synchronous = 2");
208       if (SQLITE_DONE != statement.step()) {
209         LOG(ERROR) << sqlite3_errmsg(scoped_handle.get());
210         return false;
211       }
212     }
213     sqlite3_busy_timeout(scoped_handle.release(),
214                          kDirectoryBackingStoreBusyTimeoutMs);
215 #if defined(OS_WIN)
216     // Do not index this file. Scanning can occur every time we close the file,
217     // which causes long delays in SQLite's file locking.
218     const DWORD attrs = GetFileAttributes(backing_filepath_.value().c_str());
219     const BOOL attrs_set =
220       SetFileAttributes(backing_filepath_.value().c_str(),
221                         attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
222 #endif
223 
224     return true;
225   }
226   return false;
227 }
228 
CheckIntegrity(sqlite3 * handle,string * error) const229 bool DirectoryBackingStore::CheckIntegrity(sqlite3* handle, string* error)
230     const {
231   SQLStatement statement;
232   statement.prepare(handle, "PRAGMA integrity_check(1)");
233   if (SQLITE_ROW != statement.step()) {
234     *error =  sqlite3_errmsg(handle);
235     return false;
236   }
237   string integrity_result = statement.column_text(0);
238   if (integrity_result != "ok") {
239     *error = integrity_result;
240     return false;
241   }
242   return true;
243 }
244 
DoLoad(MetahandlesIndex * entry_bucket,Directory::KernelLoadInfo * kernel_load_info)245 DirOpenResult DirectoryBackingStore::DoLoad(MetahandlesIndex* entry_bucket,
246     Directory::KernelLoadInfo* kernel_load_info) {
247   {
248     DirOpenResult result = InitializeTables();
249     if (result != OPENED)
250       return result;
251   }
252 
253   if (!DropDeletedEntries())
254     return FAILED_DATABASE_CORRUPT;
255   if (!LoadEntries(entry_bucket))
256     return FAILED_DATABASE_CORRUPT;
257   if (!LoadInfo(kernel_load_info))
258     return FAILED_DATABASE_CORRUPT;
259 
260   return OPENED;
261 }
262 
Load(MetahandlesIndex * entry_bucket,Directory::KernelLoadInfo * kernel_load_info)263 DirOpenResult DirectoryBackingStore::Load(MetahandlesIndex* entry_bucket,
264     Directory::KernelLoadInfo* kernel_load_info) {
265 
266   // Open database handle.
267   if (!BeginLoad())
268     return FAILED_OPEN_DATABASE;
269 
270   // Load data from the database.
271   DirOpenResult result = DoLoad(entry_bucket, kernel_load_info);
272 
273   // Clean up partial results after failure.
274   if (result != OPENED)
275     STLDeleteElements(entry_bucket);
276 
277   // Close database handle.
278   EndLoad();
279 
280   return result;
281 }
282 
BeginLoad()283 bool DirectoryBackingStore::BeginLoad() {
284   DCHECK(load_dbhandle_ == NULL);
285   bool ret = OpenAndConfigureHandleHelper(&load_dbhandle_);
286   if (ret)
287     return true;
288   // Something's gone wrong. Nuke the database and try again.
289   using ::operator<<;  // For string16.
290   LOG(ERROR) << "Sync database " << backing_filepath_.value()
291              << " corrupt. Deleting and recreating.";
292   file_util::Delete(backing_filepath_, false);
293   bool failed_again = !OpenAndConfigureHandleHelper(&load_dbhandle_);
294 
295   // Using failed_again here lets us distinguish from cases where corruption
296   // occurred even when re-opening a fresh directory (they'll go in a separate
297   // double weight histogram bucket).  Failing twice in a row means we disable
298   // sync, so it's useful to see this number separately.
299   int bucket = failed_again ? 2 : 1;
300 #if defined(OS_WIN)
301   UMA_HISTOGRAM_COUNTS_100("Sync.DirectoryOpenFailedWin", bucket);
302 #elif defined(OS_MACOSX)
303   UMA_HISTOGRAM_COUNTS_100("Sync.DirectoryOpenFailedMac", bucket);
304 #else
305   UMA_HISTOGRAM_COUNTS_100("Sync.DirectoryOpenFailedNotWinMac", bucket);
306 
307 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
308   UMA_HISTOGRAM_COUNTS_100("Sync.DirectoryOpenFailedLinux", bucket);
309 #elif defined(OS_CHROMEOS)
310   UMA_HISTOGRAM_COUNTS_100("Sync.DirectoryOpenFailedCros", bucket);
311 #else
312   UMA_HISTOGRAM_COUNTS_100("Sync.DirectoryOpenFailedOther", bucket);
313 #endif  // OS_LINUX && !OS_CHROMEOS
314 #endif  // OS_WIN
315   return !failed_again;
316 }
317 
EndLoad()318 void DirectoryBackingStore::EndLoad() {
319   sqlite3_close(load_dbhandle_);
320   load_dbhandle_ = NULL;  // No longer used.
321 }
322 
EndSave()323 void DirectoryBackingStore::EndSave() {
324   sqlite3_close(save_dbhandle_);
325   save_dbhandle_ = NULL;
326 }
327 
DeleteEntries(const MetahandleSet & handles)328 bool DirectoryBackingStore::DeleteEntries(const MetahandleSet& handles) {
329   if (handles.empty())
330     return true;
331 
332   sqlite3* dbhandle = LazyGetSaveHandle();
333 
334   string query = "DELETE FROM metas WHERE metahandle IN (";
335   for (MetahandleSet::const_iterator it = handles.begin(); it != handles.end();
336        ++it) {
337     if (it != handles.begin())
338       query.append(",");
339     query.append(base::Int64ToString(*it));
340   }
341   query.append(")");
342   SQLStatement statement;
343   int result = statement.prepare(dbhandle, query.data(), query.size());
344   if (SQLITE_OK == result)
345     result = statement.step();
346 
347   return SQLITE_DONE == result;
348 }
349 
SaveChanges(const Directory::SaveChangesSnapshot & snapshot)350 bool DirectoryBackingStore::SaveChanges(
351     const Directory::SaveChangesSnapshot& snapshot) {
352   sqlite3* dbhandle = LazyGetSaveHandle();
353 
354   // SQLTransaction::BeginExclusive causes a disk write to occur. This is not
355   // something that should happen every 10 seconds when this function runs, so
356   // just stop here if there's nothing to save.
357   bool save_info =
358     (Directory::KERNEL_SHARE_INFO_DIRTY == snapshot.kernel_info_status);
359   if (snapshot.dirty_metas.size() < 1 && !save_info)
360     return true;
361 
362   SQLTransaction transaction(dbhandle);
363   if (SQLITE_OK != transaction.BeginExclusive())
364     return false;
365 
366   for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
367        i != snapshot.dirty_metas.end(); ++i) {
368     DCHECK(i->is_dirty());
369     if (!SaveEntryToDB(*i))
370       return false;
371   }
372 
373   if (!DeleteEntries(snapshot.metahandles_to_purge))
374     return false;
375 
376   if (save_info) {
377     const Directory::PersistedKernelInfo& info = snapshot.kernel_info;
378     SQLStatement update;
379     update.prepare(dbhandle, "UPDATE share_info "
380                    "SET store_birthday = ?, "
381                    "next_id = ?, "
382                    "notification_state = ?, "
383                    "autofill_migration_state = ?, "
384                    "bookmarks_added_during_autofill_migration = ?, "
385                    "autofill_migration_time = ?, "
386                    "autofill_entries_added_during_migration = ?, "
387                    "autofill_profiles_added_during_migration = ? ");
388 
389     const syncable::AutofillMigrationDebugInfo& debug_info =
390         info.autofill_migration_debug_info;
391     update.bind_string(0, info.store_birthday);
392     update.bind_int64(1, info.next_id);
393     update.bind_blob(2, info.notification_state.data(),
394                      info.notification_state.size());
395     update.bind_int(3, info.autofill_migration_state);
396     update.bind_int(4,
397         debug_info.bookmarks_added_during_migration);
398     update.bind_int64(5,
399         debug_info.autofill_migration_time);
400     update.bind_int(6,
401         debug_info.autofill_entries_added_during_migration);
402     update.bind_int(7,
403         debug_info.autofill_profile_added_during_migration);
404 
405     if (!(SQLITE_DONE == update.step() &&
406           SQLITE_OK == update.reset() &&
407           1 == update.changes())) {
408       return false;
409     }
410 
411     for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
412       SQLStatement op;
413       op.prepare(dbhandle, "INSERT OR REPLACE INTO models (model_id, "
414       "progress_marker, initial_sync_ended) VALUES ( ?, ?, ?)");
415       // We persist not ModelType but rather a protobuf-derived ID.
416       string model_id = ModelTypeEnumToModelId(ModelTypeFromInt(i));
417       string progress_marker;
418       info.download_progress[i].SerializeToString(&progress_marker);
419       op.bind_blob(0, model_id.data(), model_id.length());
420       op.bind_blob(1, progress_marker.data(), progress_marker.length());
421       op.bind_bool(2, info.initial_sync_ended[i]);
422 
423       if (!(SQLITE_DONE == op.step() &&
424             SQLITE_OK == op.reset() &&
425             1 == op.changes())) {
426         return false;
427       }
428     }
429   }
430 
431   return (SQLITE_OK == transaction.Commit());
432 }
433 
InitializeTables()434 DirOpenResult DirectoryBackingStore::InitializeTables() {
435   SQLTransaction transaction(load_dbhandle_);
436   if (SQLITE_OK != transaction.BeginExclusive()) {
437     return FAILED_DISK_FULL;
438   }
439   int version_on_disk = GetVersion();
440   int last_result = SQLITE_DONE;
441 
442   // Upgrade from version 67. Version 67 was widely distributed as the original
443   // Bookmark Sync release. Version 68 removed unique naming.
444   if (version_on_disk == 67) {
445     if (MigrateVersion67To68())
446       version_on_disk = 68;
447   }
448   // Version 69 introduced additional datatypes.
449   if (version_on_disk == 68) {
450     if (MigrateVersion68To69())
451       version_on_disk = 69;
452   }
453 
454   if (version_on_disk == 69) {
455     if (MigrateVersion69To70())
456       version_on_disk = 70;
457   }
458 
459   // Version 71 changed the sync progress information to be per-datatype.
460   if (version_on_disk == 70) {
461     if (MigrateVersion70To71())
462       version_on_disk = 71;
463   }
464 
465   // Version 72 removed extended attributes, a legacy way to do extensible
466   // key/value information, stored in their own table.
467   if (version_on_disk == 71) {
468     if (MigrateVersion71To72())
469       version_on_disk = 72;
470   }
471 
472   // Version 73 added a field for notification state.
473   if (version_on_disk == 72) {
474     if (MigrateVersion72To73())
475       version_on_disk = 73;
476   }
477 
478   // Version 74 added state for the autofill migration.
479   if (version_on_disk == 73) {
480     if (MigrateVersion73To74())
481       version_on_disk = 74;
482   }
483 
484   // Version 75 migrated from int64-based timestamps to per-datatype tokens.
485   if (version_on_disk == 74) {
486     if (MigrateVersion74To75())
487       version_on_disk = 75;
488   }
489 
490   // If one of the migrations requested it, drop columns that aren't current.
491   // It's only safe to do this after migrating all the way to the current
492   // version.
493   if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) {
494     if (!RefreshColumns())
495       version_on_disk = 0;
496   }
497 
498   // A final, alternative catch-all migration to simply re-sync everything.
499   if (version_on_disk != kCurrentDBVersion) {
500     if (version_on_disk > kCurrentDBVersion) {
501       transaction.Rollback();
502       return FAILED_NEWER_VERSION;
503     }
504     // Fallback (re-sync everything) migration path.
505     VLOG(1) << "Old/null sync database, version " << version_on_disk;
506     // Delete the existing database (if any), and create a fresh one.
507     DropAllTables();
508     last_result = CreateTables();
509   }
510   if (SQLITE_DONE == last_result) {
511     {
512       SQLStatement statement;
513       statement.prepare(load_dbhandle_,
514           "SELECT db_create_version, db_create_time FROM share_info");
515       if (SQLITE_ROW != statement.step()) {
516         transaction.Rollback();
517         return FAILED_DISK_FULL;
518       }
519       string db_create_version = statement.column_text(0);
520       int db_create_time = statement.column_int(1);
521       statement.reset();
522       VLOG(1) << "DB created at " << db_create_time << " by version " <<
523           db_create_version;
524     }
525     // COMMIT TRANSACTION rolls back on failure.
526     if (SQLITE_OK == transaction.Commit())
527       return OPENED;
528   } else {
529     transaction.Rollback();
530   }
531   return FAILED_DISK_FULL;
532 }
533 
RefreshColumns()534 bool DirectoryBackingStore::RefreshColumns() {
535   DCHECK(needs_column_refresh_);
536 
537   // Create a new table named temp_metas.
538   SafeDropTable("temp_metas");
539   if (CreateMetasTable(true) != SQLITE_DONE)
540     return false;
541 
542   // Populate temp_metas from metas.
543   std::string query = "INSERT INTO temp_metas (";
544   AppendColumnList(&query);
545   query.append(") SELECT ");
546   AppendColumnList(&query);
547   query.append(" FROM metas");
548   if (ExecQuery(load_dbhandle_, query.c_str()) != SQLITE_DONE)
549     return false;
550 
551   // Drop metas.
552   SafeDropTable("metas");
553 
554   // Rename temp_metas -> metas.
555   int result = ExecQuery(load_dbhandle_,
556                          "ALTER TABLE temp_metas RENAME TO metas");
557   if (result != SQLITE_DONE)
558     return false;
559   needs_column_refresh_ = false;
560   return true;
561 }
562 
LoadEntries(MetahandlesIndex * entry_bucket)563 bool DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) {
564   string select;
565   select.reserve(kUpdateStatementBufferSize);
566   select.append("SELECT ");
567   AppendColumnList(&select);
568   select.append(" FROM metas ");
569   SQLStatement statement;
570   statement.prepare(load_dbhandle_, select.c_str());
571   base::hash_set<int64> handles;
572   EntryKernel* kernel = NULL;
573   int query_result;
574   while (SQLITE_ROW == (query_result = UnpackEntry(&statement, &kernel))) {
575     DCHECK(handles.insert(kernel->ref(META_HANDLE)).second);  // Only in debug.
576     entry_bucket->insert(kernel);
577   }
578   return SQLITE_DONE == query_result;
579 }
580 
LoadInfo(Directory::KernelLoadInfo * info)581 bool DirectoryBackingStore::LoadInfo(Directory::KernelLoadInfo* info) {
582   {
583     SQLStatement query;
584     query.prepare(load_dbhandle_,
585                   "SELECT store_birthday, next_id, cache_guid, "
586                   "notification_state, autofill_migration_state, "
587                   "bookmarks_added_during_autofill_migration, "
588                   "autofill_migration_time, "
589                   "autofill_entries_added_during_migration, "
590                   "autofill_profiles_added_during_migration "
591                   "FROM share_info");
592     if (SQLITE_ROW != query.step())
593       return false;
594     info->kernel_info.store_birthday = query.column_string(0);
595     info->kernel_info.next_id = query.column_int64(1);
596     info->cache_guid = query.column_string(2);
597     query.column_blob_as_string(3, &info->kernel_info.notification_state);
598     info->kernel_info.autofill_migration_state =
599         static_cast<AutofillMigrationState> (query.column_int(4));
600     syncable::AutofillMigrationDebugInfo& debug_info =
601         info->kernel_info.autofill_migration_debug_info;
602     debug_info.bookmarks_added_during_migration =
603       query.column_int(5);
604     debug_info.autofill_migration_time =
605       query.column_int64(6);
606     debug_info.autofill_entries_added_during_migration =
607       query.column_int(7);
608     debug_info.autofill_profile_added_during_migration =
609       query.column_int(8);
610   }
611   {
612     SQLStatement query;
613     query.prepare(load_dbhandle_,
614         "SELECT model_id, progress_marker, initial_sync_ended "
615         "FROM models");
616     while (SQLITE_ROW == query.step()) {
617       ModelType type = ModelIdToModelTypeEnum(query.column_blob(0),
618                                               query.column_bytes(0));
619       if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) {
620         info->kernel_info.download_progress[type].ParseFromArray(
621             query.column_blob(1), query.column_bytes(1));
622         info->kernel_info.initial_sync_ended[type] = query.column_bool(2);
623       }
624     }
625   }
626   {
627     SQLStatement query;
628     query.prepare(load_dbhandle_,
629                   "SELECT MAX(metahandle) FROM metas");
630     if (SQLITE_ROW != query.step())
631       return false;
632     info->max_metahandle = query.column_int64(0);
633   }
634   return true;
635 }
636 
SaveEntryToDB(const EntryKernel & entry)637 bool DirectoryBackingStore::SaveEntryToDB(const EntryKernel& entry) {
638   DCHECK(save_dbhandle_);
639   string query;
640   query.reserve(kUpdateStatementBufferSize);
641   query.append("INSERT OR REPLACE INTO metas ");
642   string values;
643   values.reserve(kUpdateStatementBufferSize);
644   values.append("VALUES ");
645   const char* separator = "( ";
646   int i = 0;
647   for (i = BEGIN_FIELDS; i < PROTO_FIELDS_END; ++i) {
648     query.append(separator);
649     values.append(separator);
650     separator = ", ";
651     query.append(ColumnName(i));
652     values.append("?");
653   }
654 
655   query.append(" ) ");
656   values.append(" )");
657   query.append(values);
658   SQLStatement statement;
659   statement.prepare(save_dbhandle_, query.c_str());
660   BindFields(entry, &statement);
661   return (SQLITE_DONE == statement.step() &&
662           SQLITE_OK == statement.reset() &&
663           1 == statement.changes());
664 }
665 
DropDeletedEntries()666 bool DirectoryBackingStore::DropDeletedEntries() {
667   static const char delete_metas[] = "DELETE FROM metas WHERE metahandle IN "
668                                      "(SELECT metahandle from death_row)";
669   // Put all statements into a transaction for better performance
670   SQLTransaction transaction(load_dbhandle_);
671   transaction.Begin();
672   if (SQLITE_DONE != ExecQuery(
673                          load_dbhandle_,
674                          "CREATE TEMP TABLE death_row (metahandle BIGINT)")) {
675     return false;
676   }
677   if (SQLITE_DONE != ExecQuery(load_dbhandle_,
678                                "INSERT INTO death_row "
679                                "SELECT metahandle from metas WHERE is_del > 0 "
680                                " AND is_unsynced < 1"
681                                " AND is_unapplied_update < 1")) {
682     return false;
683   }
684   if (SQLITE_DONE != ExecQuery(load_dbhandle_, delete_metas)) {
685     return false;
686   }
687   if (SQLITE_DONE != ExecQuery(load_dbhandle_, "DROP TABLE death_row")) {
688     return false;
689   }
690   transaction.Commit();
691   return true;
692 }
693 
SafeDropTable(const char * table_name)694 int DirectoryBackingStore::SafeDropTable(const char* table_name) {
695   string query = "DROP TABLE IF EXISTS ";
696   query.append(table_name);
697   SQLStatement statement;
698   int result = statement.prepare(load_dbhandle_, query.data(),
699                                  query.size());
700   if (SQLITE_OK == result) {
701     result = statement.step();
702     if (SQLITE_DONE == result)
703       statement.finalize();
704   }
705 
706   return result;
707 }
708 
DropAllTables()709 void DirectoryBackingStore::DropAllTables() {
710   SafeDropTable("metas");
711   SafeDropTable("temp_metas");
712   SafeDropTable("share_info");
713   SafeDropTable("temp_share_info");
714   SafeDropTable("share_version");
715   SafeDropTable("extended_attributes");
716   SafeDropTable("models");
717   SafeDropTable("temp_models");
718   needs_column_refresh_ = false;
719 }
720 
721 // static
ModelIdToModelTypeEnum(const void * data,int size)722 ModelType DirectoryBackingStore::ModelIdToModelTypeEnum(
723     const void* data, int size) {
724   sync_pb::EntitySpecifics specifics;
725   if (!specifics.ParseFromArray(data, size))
726     return syncable::UNSPECIFIED;
727   return syncable::GetModelTypeFromSpecifics(specifics);
728 }
729 
730 // static
ModelTypeEnumToModelId(ModelType model_type)731 string DirectoryBackingStore::ModelTypeEnumToModelId(ModelType model_type) {
732   sync_pb::EntitySpecifics specifics;
733   syncable::AddDefaultExtensionValue(model_type, &specifics);
734   return specifics.SerializeAsString();
735 }
736 
MigrateToSpecifics(const char * old_columns,const char * specifics_column,void (* handler_function)(SQLStatement * old_value_query,int old_value_column,sync_pb::EntitySpecifics * mutable_new_value))737 bool DirectoryBackingStore::MigrateToSpecifics(
738     const char* old_columns,
739     const char* specifics_column,
740     void (*handler_function)(SQLStatement* old_value_query,
741                              int old_value_column,
742                              sync_pb::EntitySpecifics* mutable_new_value)) {
743   std::string query_sql = StringPrintf("SELECT metahandle, %s, %s FROM metas",
744                                        specifics_column, old_columns);
745   std::string update_sql = StringPrintf(
746       "UPDATE metas SET %s = ? WHERE metahandle = ?", specifics_column);
747   SQLStatement query;
748   query.prepare(load_dbhandle_, query_sql.c_str());
749   while (query.step() == SQLITE_ROW) {
750     int64 metahandle = query.column_int64(0);
751     std::string new_value_bytes;
752     query.column_blob_as_string(1, &new_value_bytes);
753     sync_pb::EntitySpecifics new_value;
754     new_value.ParseFromString(new_value_bytes);
755     handler_function(&query, 2, &new_value);
756     new_value.SerializeToString(&new_value_bytes);
757 
758     SQLStatement update;
759     update.prepare(load_dbhandle_, update_sql.data(), update_sql.length());
760     update.bind_blob(0, new_value_bytes.data(), new_value_bytes.length());
761     update.bind_int64(1, metahandle);
762     if (update.step() != SQLITE_DONE) {
763       NOTREACHED();
764       return false;
765     }
766   }
767   return true;
768 }
769 
AddColumn(const ColumnSpec * column)770 bool DirectoryBackingStore::AddColumn(const ColumnSpec* column) {
771   SQLStatement add_column;
772   std::string sql = StringPrintf("ALTER TABLE metas ADD COLUMN %s %s",
773                                  column->name, column->spec);
774   add_column.prepare(load_dbhandle_, sql.c_str());
775   return add_column.step() == SQLITE_DONE;
776 }
777 
SetVersion(int version)778 bool DirectoryBackingStore::SetVersion(int version) {
779   SQLStatement statement;
780   statement.prepare(load_dbhandle_, "UPDATE share_version SET data = ?");
781   statement.bind_int(0, version);
782   return statement.step() == SQLITE_DONE;
783 }
784 
GetVersion()785 int DirectoryBackingStore::GetVersion() {
786   if (!sqlite_utils::DoesSqliteTableExist(load_dbhandle_, "share_version"))
787     return 0;
788   SQLStatement version_query;
789   version_query.prepare(load_dbhandle_, "SELECT data from share_version");
790   if (SQLITE_ROW != version_query.step())
791     return 0;
792   int value = version_query.column_int(0);
793   if (version_query.reset() != SQLITE_OK)
794     return 0;
795   return value;
796 }
797 
MigrateVersion67To68()798 bool DirectoryBackingStore::MigrateVersion67To68() {
799   // This change simply removed three columns:
800   //   string NAME
801   //   string UNSANITIZED_NAME
802   //   string SERVER_NAME
803   // No data migration is necessary, but we should do a column refresh.
804   SetVersion(68);
805   needs_column_refresh_ = true;
806   return true;
807 }
808 
MigrateVersion69To70()809 bool DirectoryBackingStore::MigrateVersion69To70() {
810   // Added "unique_client_tag", renamed "singleton_tag" to unique_server_tag
811   SetVersion(70);
812   // We use these metas column names but if in the future
813   // we rename the column again, we need to inline the old
814   // intermediate name / column spec.
815   if (!AddColumn(&g_metas_columns[UNIQUE_SERVER_TAG])) {
816     return false;
817   }
818   if (!AddColumn(&g_metas_columns[UNIQUE_CLIENT_TAG])) {
819     return false;
820   }
821   needs_column_refresh_ = true;
822 
823   SQLStatement statement;
824   statement.prepare(load_dbhandle_,
825       "UPDATE metas SET unique_server_tag = singleton_tag");
826   return statement.step() == SQLITE_DONE;
827 }
828 
829 namespace {
830 
831 // Callback passed to MigrateToSpecifics for the v68->v69 migration.  See
832 // MigrateVersion68To69().
EncodeBookmarkURLAndFavicon(SQLStatement * old_value_query,int old_value_column,sync_pb::EntitySpecifics * mutable_new_value)833 void EncodeBookmarkURLAndFavicon(SQLStatement* old_value_query,
834                                  int old_value_column,
835                                  sync_pb::EntitySpecifics* mutable_new_value) {
836   // Extract data from the column trio we expect.
837   bool old_is_bookmark_object = old_value_query->column_bool(old_value_column);
838   std::string old_url = old_value_query->column_string(old_value_column + 1);
839   std::string old_favicon;
840   old_value_query->column_blob_as_string(old_value_column + 2, &old_favicon);
841   bool old_is_dir = old_value_query->column_bool(old_value_column + 3);
842 
843   if (old_is_bookmark_object) {
844     sync_pb::BookmarkSpecifics* bookmark_data =
845         mutable_new_value->MutableExtension(sync_pb::bookmark);
846     if (!old_is_dir) {
847       bookmark_data->set_url(old_url);
848       bookmark_data->set_favicon(old_favicon);
849     }
850   }
851 }
852 
853 }  // namespace
854 
MigrateVersion68To69()855 bool DirectoryBackingStore::MigrateVersion68To69() {
856   // In Version 68, there were columns on table 'metas':
857   //   string BOOKMARK_URL
858   //   string SERVER_BOOKMARK_URL
859   //   blob BOOKMARK_FAVICON
860   //   blob SERVER_BOOKMARK_FAVICON
861   // In version 69, these columns went away in favor of storing
862   // a serialized EntrySpecifics protobuf in the columns:
863   //   protobuf blob SPECIFICS
864   //   protobuf blob SERVER_SPECIFICS
865   // For bookmarks, EntrySpecifics is extended as per
866   // bookmark_specifics.proto. This migration converts bookmarks from the
867   // former scheme to the latter scheme.
868 
869   // First, add the two new columns to the schema.
870   if (!AddColumn(&g_metas_columns[SPECIFICS]))
871     return false;
872   if (!AddColumn(&g_metas_columns[SERVER_SPECIFICS]))
873     return false;
874 
875   // Next, fold data from the old columns into the new protobuf columns.
876   if (!MigrateToSpecifics(("is_bookmark_object, bookmark_url, "
877                            "bookmark_favicon, is_dir"),
878                           "specifics",
879                           &EncodeBookmarkURLAndFavicon)) {
880     return false;
881   }
882   if (!MigrateToSpecifics(("server_is_bookmark_object, "
883                            "server_bookmark_url, "
884                            "server_bookmark_favicon, "
885                            "server_is_dir"),
886                           "server_specifics",
887                           &EncodeBookmarkURLAndFavicon)) {
888     return false;
889   }
890 
891   // Lastly, fix up the "Google Chrome" folder, which is of the TOP_LEVEL_FOLDER
892   // ModelType: it shouldn't have BookmarkSpecifics.
893   SQLStatement clear_permanent_items;
894   clear_permanent_items.prepare(load_dbhandle_,
895       "UPDATE metas SET specifics = NULL, server_specifics = NULL WHERE "
896       "singleton_tag IN ('google_chrome')");
897   if (clear_permanent_items.step() != SQLITE_DONE)
898     return false;
899 
900   SetVersion(69);
901   needs_column_refresh_ = true;  // Trigger deletion of old columns.
902   return true;
903 }
904 
905 // Version 71, the columns 'initial_sync_ended' and 'last_sync_timestamp'
906 // were removed from the share_info table.  They were replaced by
907 // the 'models' table, which has these values on a per-datatype basis.
MigrateVersion70To71()908 bool DirectoryBackingStore::MigrateVersion70To71() {
909   if (SQLITE_DONE != CreateV71ModelsTable())
910     return false;
911 
912   // Move data from the old share_info columns to the new models table.
913   {
914     SQLStatement fetch;
915     fetch.prepare(load_dbhandle_,
916         "SELECT last_sync_timestamp, initial_sync_ended FROM share_info");
917 
918     if (SQLITE_ROW != fetch.step())
919       return false;
920     int64 last_sync_timestamp = fetch.column_int64(0);
921     bool initial_sync_ended = fetch.column_bool(1);
922     if (SQLITE_DONE != fetch.step())
923       return false;
924     SQLStatement update;
925     update.prepare(load_dbhandle_, "INSERT INTO models (model_id, "
926         "last_download_timestamp, initial_sync_ended) VALUES (?, ?, ?)");
927     string bookmark_model_id = ModelTypeEnumToModelId(BOOKMARKS);
928     update.bind_blob(0, bookmark_model_id.data(), bookmark_model_id.size());
929     update.bind_int64(1, last_sync_timestamp);
930     update.bind_bool(2, initial_sync_ended);
931     if (SQLITE_DONE != update.step())
932       return false;
933   }
934 
935   // Drop the columns from the old share_info table via a temp table.
936   const bool kCreateAsTempShareInfo = true;
937 
938   int result =
939       CreateShareInfoTableVersion71(kCreateAsTempShareInfo);
940   if (result != SQLITE_DONE)
941     return false;
942   result = ExecQuery(load_dbhandle_,
943                      "INSERT INTO temp_share_info (id, name, store_birthday, "
944                      "db_create_version, db_create_time, next_id, cache_guid) "
945                      "SELECT id, name, store_birthday, db_create_version, "
946                      "db_create_time, next_id, cache_guid FROM share_info");
947   if (result != SQLITE_DONE)
948     return false;
949   SafeDropTable("share_info");
950   result = ExecQuery(load_dbhandle_,
951       "ALTER TABLE temp_share_info RENAME TO share_info");
952   if (result != SQLITE_DONE)
953     return false;
954   SetVersion(71);
955   return true;
956 }
957 
MigrateVersion71To72()958 bool DirectoryBackingStore::MigrateVersion71To72() {
959   // Version 72 removed a table 'extended_attributes', whose
960   // contents didn't matter.
961   SafeDropTable("extended_attributes");
962   SetVersion(72);
963   return true;
964 }
965 
MigrateVersion72To73()966 bool DirectoryBackingStore::MigrateVersion72To73() {
967   // Version 73 added one column to the table 'share_info': notification_state
968   int result =
969       ExecQuery(load_dbhandle_,
970                 "ALTER TABLE share_info ADD COLUMN notification_state BLOB");
971   if (result != SQLITE_DONE)
972     return false;
973   SetVersion(73);
974   return true;
975 }
976 
MigrateVersion73To74()977 bool DirectoryBackingStore::MigrateVersion73To74() {
978   // Version 74 added the following columns to the table 'share_info':
979   //   autofill_migration_state
980   //   bookmarks_added_during_autofill_migration
981   //   autofill_migration_time
982   //   autofill_entries_added_during_migration
983   //   autofill_profiles_added_during_migration
984 
985   int result =
986       ExecQuery(load_dbhandle_,
987                 "ALTER TABLE share_info ADD COLUMN autofill_migration_state "
988                 "INT default 0");
989   if (result != SQLITE_DONE)
990     return false;
991 
992   result = ExecQuery(load_dbhandle_,
993                 "ALTER TABLE share_info ADD COLUMN "
994                 "bookmarks_added_during_autofill_migration "
995                 "INT default 0");
996 
997   if (result != SQLITE_DONE)
998     return false;
999 
1000   result = ExecQuery(load_dbhandle_,
1001                 "ALTER TABLE share_info ADD COLUMN autofill_migration_time "
1002                 "INT default 0");
1003 
1004   if (result != SQLITE_DONE)
1005     return false;
1006 
1007   result = ExecQuery(load_dbhandle_,
1008                 "ALTER TABLE share_info ADD COLUMN "
1009                 "autofill_entries_added_during_migration "
1010                 "INT default 0");
1011 
1012   if (result != SQLITE_DONE)
1013     return false;
1014 
1015   result = ExecQuery(load_dbhandle_,
1016                 "ALTER TABLE share_info ADD COLUMN "
1017                 "autofill_profiles_added_during_migration "
1018                 "INT default 0");
1019 
1020   if (result != SQLITE_DONE)
1021     return false;
1022 
1023   SetVersion(74);
1024   return true;
1025 }
1026 
MigrateVersion74To75()1027 bool DirectoryBackingStore::MigrateVersion74To75() {
1028   // In version 74, there was a table 'models':
1029   //     blob model_id (entity specifics, primary key)
1030   //     int last_download_timestamp
1031   //     boolean initial_sync_ended
1032   // In version 75, we deprecated the integer-valued last_download_timestamp,
1033   // using insted a protobuf-valued progress_marker field:
1034   //     blob progress_marker
1035   // The progress_marker values are initialized from the value of
1036   // last_download_timestamp, thereby preserving the download state.
1037 
1038   // Move aside the old table and create a new empty one at the current schema.
1039   if (SQLITE_DONE != ExecQuery(load_dbhandle_,
1040           "ALTER TABLE models RENAME TO temp_models")) {
1041     return false;
1042   }
1043   if (!CreateModelsTable())
1044     return false;
1045 
1046   SQLStatement query;
1047   query.prepare(load_dbhandle_,
1048       "SELECT model_id, last_download_timestamp, initial_sync_ended "
1049       "FROM temp_models");
1050   while (SQLITE_ROW == query.step()) {
1051     ModelType type = ModelIdToModelTypeEnum(query.column_blob(0),
1052                                             query.column_bytes(0));
1053     if (type != UNSPECIFIED) {
1054       // Set the |timestamp_token_for_migration| on a new
1055       // DataTypeProgressMarker, using the old value of last_download_timestamp.
1056       // The server will turn this into a real token on our behalf the next
1057       // time we check for updates.
1058       sync_pb::DataTypeProgressMarker progress_marker;
1059       progress_marker.set_data_type_id(
1060           GetExtensionFieldNumberFromModelType(type));
1061       progress_marker.set_timestamp_token_for_migration(query.column_int64(1));
1062       std::string progress_blob;
1063       progress_marker.SerializeToString(&progress_blob);
1064 
1065       SQLStatement update;
1066       update.prepare(load_dbhandle_, "INSERT INTO models (model_id, "
1067           "progress_marker, initial_sync_ended) VALUES (?, ?, ?)");
1068       update.bind_blob(0, query.column_blob(0), query.column_bytes(0));
1069       update.bind_blob(1, progress_blob.data(), progress_blob.length());
1070       update.bind_bool(2, query.column_bool(2));
1071       if (SQLITE_DONE != update.step())
1072         return false;
1073     }
1074   }
1075   // Drop the old table.
1076   SafeDropTable("temp_models");
1077 
1078   SetVersion(75);
1079   return true;
1080 }
1081 
CreateTables()1082 int DirectoryBackingStore::CreateTables() {
1083   VLOG(1) << "First run, creating tables";
1084   // Create two little tables share_version and share_info
1085   int result = ExecQuery(load_dbhandle_,
1086                          "CREATE TABLE share_version ("
1087                          "id VARCHAR(128) primary key, data INT)");
1088   if (result != SQLITE_DONE)
1089     return result;
1090   {
1091     SQLStatement statement;
1092     statement.prepare(load_dbhandle_, "INSERT INTO share_version VALUES(?, ?)");
1093     statement.bind_string(0, dir_name_);
1094     statement.bind_int(1, kCurrentDBVersion);
1095     result = statement.step();
1096   }
1097   if (result != SQLITE_DONE)
1098     return result;
1099 
1100   const bool kCreateAsTempShareInfo = false;
1101   result =
1102       CreateShareInfoTable(kCreateAsTempShareInfo);
1103   if (result != SQLITE_DONE)
1104     return result;
1105   {
1106     SQLStatement statement;
1107     statement.prepare(load_dbhandle_, "INSERT INTO share_info VALUES"
1108                                       "(?, "  // id
1109                                       "?, "   // name
1110                                       "?, "   // store_birthday
1111                                       "?, "   // db_create_version
1112                                       "?, "   // db_create_time
1113                                       "-2, "  // next_id
1114                                       "?, "   // cache_guid
1115                                       "?, "   // autofill_migration_state
1116                                       "?, "   // bookmarks_added
1117                                               // _during_autofill_migration
1118                                       "?, "   // autofill_migration_time
1119                                       "?, "   // autofill_entries
1120                                               // _added_during_migration
1121                                       "?, "   // autofill_profiles_added
1122                                               // _during_migration
1123                                       "?);");  // notification_state
1124     statement.bind_string(0, dir_name_);                   // id
1125     statement.bind_string(1, dir_name_);                   // name
1126     statement.bind_string(2, "");                          // store_birthday
1127     statement.bind_string(3, SYNC_ENGINE_VERSION_STRING);  // db_create_version
1128     statement.bind_int(4, static_cast<int32>(time(0)));    // db_create_time
1129     statement.bind_string(5, GenerateCacheGUID());         // cache_guid
1130     statement.bind_int(6, 0);  // autofill_migration_state
1131     statement.bind_int(7, 0);  // autofill_migration_time
1132     statement.bind_int(8, 0);  // bookmarks_added_during_autofill_migration
1133     statement.bind_int(9, 0);  // autofill_entries_added_during_migration
1134     statement.bind_int(10, 0);  // autofill_profiles_added_during_migration
1135     statement.bind_blob(11, NULL, 0);                      // notification_state
1136     result = statement.step();
1137   }
1138   if (result != SQLITE_DONE)
1139     return result;
1140 
1141   result = CreateModelsTable();
1142   if (result != SQLITE_DONE)
1143     return result;
1144   // Create the big metas table.
1145   result = CreateMetasTable(false);
1146   if (result != SQLITE_DONE)
1147     return result;
1148   {
1149     // Insert the entry for the root into the metas table.
1150     const int64 now = Now();
1151     SQLStatement statement;
1152     statement.prepare(load_dbhandle_,
1153                       "INSERT INTO metas "
1154                       "( id, metahandle, is_dir, ctime, mtime) "
1155                       "VALUES ( \"r\", 1, 1, ?, ?)");
1156     statement.bind_int64(0, now);
1157     statement.bind_int64(1, now);
1158     result = statement.step();
1159   }
1160   return result;
1161 }
1162 
LazyGetSaveHandle()1163 sqlite3* DirectoryBackingStore::LazyGetSaveHandle() {
1164   if (!save_dbhandle_ && !OpenAndConfigureHandleHelper(&save_dbhandle_)) {
1165     NOTREACHED() << "Unable to open handle for saving";
1166     return NULL;
1167   }
1168   return save_dbhandle_;
1169 }
1170 
CreateMetasTable(bool is_temporary)1171 int DirectoryBackingStore::CreateMetasTable(bool is_temporary) {
1172   const char* name = is_temporary ? "temp_metas" : "metas";
1173   string query = "CREATE TABLE ";
1174   query.append(name);
1175   query.append(ComposeCreateTableColumnSpecs());
1176   return ExecQuery(load_dbhandle_, query.c_str());
1177 }
1178 
CreateV71ModelsTable()1179 int DirectoryBackingStore::CreateV71ModelsTable() {
1180   // This is an old schema for the Models table, used from versions 71 to 74.
1181   return ExecQuery(load_dbhandle_,
1182       "CREATE TABLE models ("
1183       "model_id BLOB primary key, "
1184       "last_download_timestamp INT, "
1185       // Gets set if the syncer ever gets updates from the
1186       // server and the server returns 0.  Lets us detect the
1187       // end of the initial sync.
1188       "initial_sync_ended BOOLEAN default 0)");
1189 }
1190 
CreateModelsTable()1191 int DirectoryBackingStore::CreateModelsTable() {
1192   // This is the current schema for the Models table, from version 75
1193   // onward.  If you change the schema, you'll probably want to double-check
1194   // the use of this function in the v74-v75 migration.
1195   return ExecQuery(load_dbhandle_,
1196       "CREATE TABLE models ("
1197       "model_id BLOB primary key, "
1198       "progress_marker BLOB, "
1199       // Gets set if the syncer ever gets updates from the
1200       // server and the server returns 0.  Lets us detect the
1201       // end of the initial sync.
1202       "initial_sync_ended BOOLEAN default 0)");
1203 }
1204 
CreateShareInfoTable(bool is_temporary)1205 int DirectoryBackingStore::CreateShareInfoTable(bool is_temporary) {
1206   const char* name = is_temporary ? "temp_share_info" : "share_info";
1207   string query = "CREATE TABLE ";
1208   query.append(name);
1209   // This is the current schema for the ShareInfo table, from version 74
1210   // onward.
1211   query.append(" ("
1212       "id TEXT primary key, "
1213       "name TEXT, "
1214       "store_birthday TEXT, "
1215       "db_create_version TEXT, "
1216       "db_create_time INT, "
1217       "next_id INT default -2, "
1218       "cache_guid TEXT, "
1219       "autofill_migration_state INT default 0, "
1220       "bookmarks_added_during_autofill_migration INT default 0, "
1221       "autofill_migration_time INT default 0, "
1222       "autofill_entries_added_during_migration INT default 0, "
1223       "autofill_profiles_added_during_migration INT default 0 ");
1224 
1225   query.append(", notification_state BLOB");
1226   query.append(")");
1227   return ExecQuery(load_dbhandle_, query.c_str());
1228 }
1229 
CreateShareInfoTableVersion71(bool is_temporary)1230 int DirectoryBackingStore::CreateShareInfoTableVersion71(
1231     bool is_temporary) {
1232   const char* name = is_temporary ? "temp_share_info" : "share_info";
1233   string query = "CREATE TABLE ";
1234   query.append(name);
1235   // This is the schema for the ShareInfo table used from versions 71 to 72.
1236   query.append(" ("
1237       "id TEXT primary key, "
1238       "name TEXT, "
1239       "store_birthday TEXT, "
1240       "db_create_version TEXT, "
1241       "db_create_time INT, "
1242       "next_id INT default -2, "
1243       "cache_guid TEXT )");
1244   return ExecQuery(load_dbhandle_, query.c_str());
1245 }
1246 }  // namespace syncable
1247